面试题答案
一键面试C++编译器自动生成的拷贝构造函数工作原理
- 浅拷贝:当没有定义拷贝构造函数时,C++编译器会自动生成一个默认的拷贝构造函数。它的工作方式是进行成员变量的逐字节拷贝。对于基本数据类型(如
int
、double
等),这意味着直接复制值。对于类类型的成员变量,会调用其对应的拷贝构造函数(如果存在),同样是逐字节拷贝。例如:
class Example {
public:
int value;
};
Example obj1;
obj1.value = 10;
Example obj2(obj1); // 调用自动生成的拷贝构造函数,obj2.value 也为10
- 位拷贝:本质上,自动生成的拷贝构造函数是对对象在内存中的二进制表示进行拷贝,不会区分对象内部数据成员的含义,只是简单地复制内存块。
需要手动实现拷贝构造函数的情况
- 资源管理:当类中包含指针成员,并且该指针指向动态分配的资源(如堆内存)时,自动生成的拷贝构造函数的浅拷贝会导致问题。因为多个对象会指向同一块内存,当其中一个对象析构释放了该内存,其他对象就会成为野指针,导致程序崩溃。例如:
class String {
public:
char* str;
int length;
String(const char* s) {
length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
}
~String() {
delete[] str;
}
};
// 此时如果使用自动生成的拷贝构造函数
String s1("Hello");
String s2(s1); // s1和s2的str指向同一块内存
s1.~String(); // s1析构,释放内存
// s2的str此时成为野指针,再访问s2.str会导致未定义行为
- 避免共享资源:有些情况下,对象内部的资源是不应该被多个对象共享的,比如文件描述符。如果使用自动生成的拷贝构造函数进行浅拷贝,多个对象将共享同一个文件描述符,这可能导致文件操作的混乱。例如:
class FileHandler {
public:
FILE* file;
FileHandler(const char* filename) {
file = fopen(filename, "r");
}
~FileHandler() {
if (file) fclose(file);
}
};
// 使用自动生成的拷贝构造函数
FileHandler f1("test.txt");
FileHandler f2(f1); // f1和f2共享同一个文件描述符
// 如果f1关闭文件,f2的file就成为无效的,后续对f2的操作会出错
手动实现拷贝构造函数示例
以String
类为例,手动实现拷贝构造函数:
class String {
public:
char* str;
int length;
String(const char* s) {
length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
}
// 手动实现拷贝构造函数
String(const String& other) {
length = other.length;
str = new char[length + 1];
strcpy(str, other.str);
}
~String() {
delete[] str;
}
};
在这个手动实现的拷贝构造函数中,为新对象分配了独立的内存空间,并复制了字符串内容,避免了浅拷贝带来的问题。