谈一谈C++类的“浅复制”和“深复制”

谈“浅复制”和“深复制”之前先介绍如下知识点:

C++类的特殊成员函数:

1.默认构造函数

2.默认析构函数

3.复制构造函数

4.赋值运算符

5.地址运算符

默认构造函数和默认析构函数,大家坑定都非常了解这里就不多说。这里主要说一说"复制构造函数"和“赋值运算符”


复制构造函数

将一个对象复制到新创对象当中。函数原型如下

Class_name(const Class_name &);


假设motto是StringBad的一个对象,一下四种声明都将调用构造函数:

1.StringBad diitto(motto);

2.StringBad diitto = motto;

3.StringBad diitto = StringBad(motto);

4.StringBad diitto = new StringBad(motto);

编译器生成对象副本时也将调用。比如说函数按值传递对象时和返回对象时,都将创建临时副本,也调用复制构造函数。

如果复制构造函数没有显式的定义将使用默认复制构造函数,不会复制静态成员


赋值运算符

将已有对象复制到另一已有对象(初始化不使用)

函数原型如下

Class_name & Class_name::Operator=(const Class_name &);

如果赋值运算符没有显式的定义将使用默认赋值运算符。


下面看一段代码

头文件strngbad.h  (包含类的声明)

#include <iostream>
#ifndef STRNGBAD_H_
#define STRNGBAD_H_
class StringBad
{
private:
    char * str;                // pointer to string
    int len;                   // length of string
    static int num_strings;    // number of objects
public:
    StringBad(const char * s); // constructor
    StringBad();               // default constructor
    ~StringBad();              // destructor
// friend function
	friend std::ostream & operator<<(std::ostream & os, 
                       const StringBad & st);
};
#endif
源文件strngbad.cpp(包含类的定义)

#include "strngbad.h"
using std::cout;

// initializing static class member
int StringBad::num_strings = 0;

// class methods

// construct StringBad from C string
StringBad::StringBad(const char * s)
{
    len = std::strlen(s);                  // set size
    str = new char[len + 1];          // allot storage
    std::strcpy(str, s);                   // initialize pointer
    num_strings++;                    // set object count
    cout << num_strings << ": \"" << str
         << "\" object created\n";    // For Your Information
}

StringBad::StringBad()                // default constructor
{
    len = 4;
    str = new char[4];
    std::strcpy(str, "C++");               // default string
    num_strings++;
    cout << num_strings << ": \"" << str
         << "\" default object created\n";  // FYI
}

StringBad::~StringBad()               // necessary destructor
{
    cout << "\"" << str << "\" object deleted, ";    // FYI
    --num_strings;                    // required
    cout << num_strings << " left\n"; // FYI
    delete [] str;                    // required
}

std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
    os << st.str;
    return os; 
}
源文件vegnews.cpp

<pre name="code" class="cpp">// vegnews.cpp -- using new and delete with classes
// compile with strngbad.cpp
#include <iostream>
using std::cout;

#include "strngbad.h"

void callme1(StringBad &);  // pass by reference
void callme2(StringBad);    // pass by value

int main()
{
	using std::endl;
	StringBad headline1("Celery");
	StringBad headline2("Lettuce");
	StringBad sports("Spinach");
	callme1(headline1);
	cout << "headline1: " << headline1 << endl;
	callme2(headline2);													//问题1
	cout << "headline2: " << headline2 << endl;

	cout << "Assign one object to another:\n";
	StringBad knot;
	knot = headline1;														//问题2
	cout << "knot: " << knot << endl;
	cout << "End of main()\n";
	return 0;
}

void callme1(StringBad & rsb)
{
	cout << "String passed by reference:\n";
	cout << "    \"" << rsb << "\"\n";
}

void callme2(StringBad sb)
{
	cout << "String passed by value:\n";
	cout << "    \"" << sb << "\"\n";
}

 
 运行结果如下: 
 


谈一谈C++类的“浅复制”和“深复制”_第1张图片

运行结果出现了问题(原因复制构造函数和赋值运算符进行的是“浅复制”)


第一处原因:

函数callme2是按值传递,产生临时对象,调用默认复制构造函数。因为“浅复制”复制的是指针,不会复制指针所指向的内容,所以把headline2.str指针值付给了新的临时对象。函数结束时,临时对象析构。该指针所指内存被释放,所以第一处乱码。

第二处原因:

knot = headline1;也是“浅复制”。headline1把指针赋给了knot。在main函数结束后对象都要进行析构,因为函数内部局部变量存储于中,按先进后出原则析构。所以knot先析构,接下来sport,再接下来headline2(前面str内存已被释放,所以是乱码),在接下来是headline1,如果能显示出来的话也是乱码(headline1.str被knot释放),但停止工作。


通俗的话说,你把自己的指针给了别人,别人先把指向的内存释放了,那你还能用个蛋蛋。


解决办法:

定义显示复制构造函数和赋值运算符,采用“深复制”。及不复制指针,而复制指针所指向的内容。用strcpy函数。

还有一问题复制构造函数计数器没有变化,可在显式定义中实现。


总结:

浅复制:只复制指针,不复制指向内容。

深复制:不复制指针,但复制指向内容。


你可能感兴趣的:(C++,类,深复制,浅复制,特殊成员函数)