zoukankan      html  css  js  c++  java
  • 构造函数constructor 与析构函数destructor(五)

    我们知道当调用默认拷贝构造函数时,一个对象对另一个对象初始化时,这时的赋值时逐成员赋值。这就是浅拷贝,当成员变量有指针时,浅拷贝就会在析构函数那里出现问题。例如下面的例子:

     1 //test.h
     2 #ifndef MYSTRING_H
     3 #define MYSTRING_H
     4 class MyString
     5 {
     6     char* m_str;
     7 public:
     8     MyString(char* str="");
     9     ~MyString();
    10     void Display();
    11 };
    12 #endif //MYSTRING_H
    13 
    14 
    15 //test.cpp
    16 #define _CRT_SECURE_NO_WARNINGS
    17 #include "MyString.h"
    18 #include<iostream>
    19 #include<cstring>
    20 using std::endl;
    21 using std::cout;
    22 
    23 MyString::MyString(char* str)
    24 {
    25     m_str = new char[strlen(str) + 1];
    26     memset(m_str, 0, strlen(str) + 1);
    27     strcpy(m_str,str);
    28 }
    29 
    30 
    31 MyString::~MyString()
    32 {
    33     cout << "destructor" << endl;
    34     delete[] m_str;
    35     //m_str = 0;
    36     
    37 }
    38 
    39 void MyString::Display(){
    40     cout << m_str << endl;
    41 }
    42 
    43 //demo.cpp
    44 #include"MyString.h"
    45 #include<iostream>
    46 using std::endl;
    47 using std::cout;
    48 
    49 int main(){
    50     MyString a("aaaaaa");
    51     MyString b = a;//调用默认拷贝构造函数,此时b对象,和a对象都是指向同一块内存空间,当析构函数调用时,那么同一块内存空间会被释放两次,从而产生运行时错误。
    52     a.Display();
    53 
    54     return 0;
    55 }

    所以此时默认拷贝构造函数就出现问题,此时应该提供自己的拷贝构造函数,来实施深拷贝。

    在类里面添加拷贝构造函数

    void MyString::AllocMemAndCpy(char* other){//类里面的工具函数,负责分配内存和拷贝内存中的值
        int len = strlen(other) + 1;
         m_str = new char[len];
        memset(m_str,0,len);
        strcpy(m_str,other);
    }
    
    MyString::MyString(const MyString& other){//拷贝构造函数
        AllocMemAndCpy(other.m_str);
    }
     1 #include"MyString.h"
     2 #include<iostream>
     3 using std::endl;
     4 using std::cout;
     5 
     6 int main(){
     7     MyString a("aaaaaa");
     8     MyString b = a;
     9     a.Display();
    10     b.Display();
    11     return 0;
    12 }

    此时运行结果就是正确的了,不会出现运行时错误了。

    注意:当类中的成员变量有指针时,就要小心了。此时可能就需要拷贝构造函数了,还有就是类中有共享内存时,这是要在析构函数中计算还剩下多少对象,只有剩下最后一个对象被销毁时才能delete那块共享内存。

    默认的赋值运算符也是浅拷贝,如果不添加自己的等号运算符,这个程序还是会出现运行时错误

     1 #include"MyString.h"
     2 #include<iostream>
     3 using std::endl;
     4 using std::cout;
     5 
     6 int main(){
     7     MyString a("aaaaaa");
     8     MyString b = a;
     9     a.Display();
    10     b.Display();
    11 
    12     MyString c;
    13     c.Display();
    14     c = a;//程序在此处调用了默认的赋值运算符(=operator),默认的赋值运算符也是浅拷贝
    15           //因此这时出现的运行时错误,也是在调用析构函数时,销毁两次同一块内存,为了使剩下成功运行,必须提供自己的等号运算符。
    16     
    17     return 0;
    18 }

    上面的代码需要特别注意的是c=a;,这种赋值会造成被删除的两个指针指向同一块内存,在析构函数调用时,同一块内存会被析构函数释放两次。但是如果c=c;,这句代码就不会造成上述的错误,因为自己给自己赋值并没有使两个对象的指针同时指向一个块内存,就自然只会调用一次析构函数,从而释放一次对象。但是为了顾及中情况,应该添加如下的赋值运算符函数。

    1 MyString& MyString::operator=(const MyString& other){
    2     if (this ==&other)//赋值对象与被赋值对象是同一个对象。即语句a=a;
    3         return *this;
    4     delete m_str;//因为赋值对象与被赋值对象不是同一个对象,因此 被赋值对象之前指向的内容应该被删除,即a=c;那么a之前指向的内存应该被释放。
    5     AllocMemAndCpy(other.m_str);
    6     return (*this);
    7 }

    禁止拷贝:

    有时候有些对象时独一无二的,那么是独一无二的对象就禁止拷贝,禁止拷贝的办法就是把operator=() 与拷贝构造函数都声明成私有的,甚至是空函数都可以

     1 #ifndef MYSTRING_H
     2 #define MYSTRING_H
     3 class MyString
     4 {
     5     char* m_str;
     6     void AllocMemAndCpy(char* other);
     7     MyString(const MyString& ms){}
     8     MyString& operator=(const MyString& other){}
     9 
    10 public:
    11     MyString(char* str="");
    12     ~MyString();
    13     
    14     
    15     void Display();
    16 };
    17 #endif //MYSTRING_H

    此时拷贝构造函数和operator=()都是声明在private中,并且都是空函数。此时Test a=c; b=c;这样的语句在编译时就会报错。

    我们知道当我们不提供构造函数时,编译器会提供默认构造函数,当我们不提供拷贝构造函数时,编译器会提供默认拷贝构造函数,那么再创建一个空的类时,编译器到底提供什么呢?

    1 class Empty {};
    2 Empty();             // 默认构造函数
    3 Empty( const Empty& );    // 默认拷贝构造函数
    4 ~Empty();             // 默认析构函数
    5 Empty& operator=( const Empty& );  // 默认赋值运算符
    6 Empty* operator&();                       // 取址运算符
    7 const Empty* operator&() const;        // 取址运算符 const

    上面的就是一个空类,我们在使用时要注意,有时候这些编译器默认提供的不适合,我们要自己写。

  • 相关阅读:
    【笔记】网易微专业-Web安全工程师-04.WEB安全实战-7.SQL回显注入
    【笔记】网易微专业-Web安全工程师-04.WEB安全实战-6.文件上传
    【笔记】网易微专业-Web安全工程师-04.WEB安全实战-5.文件包含
    【笔记】网易微专业-Web安全工程师-04.WEB安全实战-4.CSRF
    zoom和transform:scale的区别
    图片上黑色透明遮罩的实现
    如何实现两行文本增多显示省略号
    H5如何实现一行三列布局
    H5瀑布流如何实现
    H5一行显示两个正方形
  • 原文地址:https://www.cnblogs.com/cplinux/p/5619835.html
Copyright © 2011-2022 走看看