利用new在堆区创建的对象,被复制时(按对象值传参),将复制对象的指针成员,但不复制指针成员指向的缓冲区。
结果是两个对象指向同一块动态分配的内存,销毁其中一个对象时 - delete释放内存块,导致另一个对象存储的指针拷贝无效。
【编译器执行二进制复制:复制 整型、字符和原始指针 等POD数据,不复制 指针指向的内存单元】
在没有原始指针成员的情况下,不需要编写拷贝构造函数。因为编译器添加的默认拷贝构造函数 将调用成员对象的拷贝构造函数。
拷贝构造函数:一个重载的构造函数,由编写类的程序员提供。当对象被复制时,编译器会调用拷贝构造函数。
拷贝构造函数接受一个 以引用方式传入的当前类的对象 作为参数,这个参数是 原对象的别名,使用它编写拷贝构造函数,确保对所有缓冲区进行深复制。
【拷贝构造函数的参数 必须按引用传递,否则拷贝构造函数将不断调用自己(形参不断拷贝实参),直到耗尽系统的内存为止。】
//test.h
#include <iostream>
#include <stdlib.h>
#include <string>
#include <string.h>
using namespace std;
class MyString {
private:
char* buffer; //指针成员变量,都是它惹的祸~
public:
MyString(const char* initString);
MyString(const MyString& copySource);
~MyString();
int GetLen();
const char* GetStr();
};
//test.cpp
#include "class_basic.h"
MyString::MyString(const char* initString) {
buffer = NULL;
cout << "Default constructor:creating new MyString" << endl;
if (initString != NULL) {
buffer = new char[strlen(initString) + 1];
strcpy(buffer, initString);
cout << "buffer points to: 0x" << hex;
cout << (unsigned int*)buffer << endl;
}
}
MyString::MyString(const MyString& copySource) {
//调用拷贝构造函数时,形参copySource和传入的实参 指向同一块内存(引用属性),这里用buffer实现深拷贝
buffer = NULL;
cout << "Copy constructor: copying from MyString" << endl;
if (copySource.buffer != NULL) {
buffer = new char[strlen(copySource.buffer) + 1];
strcpy(buffer, copySource.buffer);
cout << "buffer points to:0x" << hex;
cout << (unsigned int*)buffer << endl;
}
}
MyString::~MyString() {
cout << "Invoking destructor, clearing up" << endl;
delete[] buffer;
}
int MyString::GetLen() {
return strlen(buffer);
}
const char* MyString::GetStr() {
return buffer;
}
//main.cpp
#include<string>
#include<iostream>
#include<stdlib.h>
#include<string>
#include "test.h"
using namespace std;
void UseMyString(MyString str) { //按值传参
cout << "String buffer in MyString is " << str.GetLen();
cout << " character long" << endl;
cout << "buffer contains: " << str.GetStr() << endl;
return;
}
int main()
{
MyString sayHello("Hello from String Class");
UseMyString(sayHello); //sayHello按值传递给形参str,调用拷贝构造函数
system("pause");
return 0;
}
//O: Default constructor:creating new MyString
// buffer points to: 0x00FD6A58
// Copy constructor: copying from MyString
// buffer points to:0x00FE0828
// String buffer in MyString is 17 character long
// buffer contains: Hello from String Class
// Invoking destructor, clearing up
如果UseMyString的参数是按引用传递,则不需要拷贝构造函数,因为引用传参时,函数里使用的就是sayHello对象自己,不需要额外开辟内存。退出UseMyString函数时,也不需要释放内存。