1. 从结构体到类的本质跨越
很多从C过渡到C++的开发者,最初往往会把类简单地理解为"带函数的结构体"。这种认知在语法层面看似合理,但在工程实践中会埋下严重隐患。让我们通过一个内存管理案例来揭示二者的本质区别:
cpp复制// C风格结构体+函数
struct String {
char* data;
int length;
};
void StringInit(struct String* s, const char* str) {
s->length = strlen(str);
s->data = (char*)malloc(s->length + 1);
strcpy(s->data, str);
}
void StringDestroy(struct String* s) {
free(s->data);
}
// C++类
class String {
private:
char* data;
int length;
public:
String(const char* str) {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
~String() {
delete[] data;
}
};
关键差异点:
- 构造/析构的自动化:类的构造函数在对象创建时自动调用,析构函数在生命周期结束时自动执行。而结构体需要手动调用初始化/清理函数
- 访问控制的强制性:类的private成员外部无法直接访问,而结构体所有成员默认公开
- 异常安全性:当中间操作抛出异常时,类的析构仍能保证资源释放,而结构体方案可能泄漏内存
经验之谈:在嵌入式系统中,我曾见过一个因混淆结构体和类而导致的内存泄漏案例。开发者用C风格管理硬件资源,在异常分支中忘记调用清理函数,最终导致设备重启才能恢复。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 深度探索构造函数家族
2.1 默认构造的陷阱与妙用
编译器生成的默认构造函数实际上执行的是"默认成员初始化":
- 内置类型:不初始化(值随机)
- 类类型成员:调用其默认构造函数
这解释了为什么如下代码可能崩溃:
cpp复制class SensorData {
int* readings; // 未初始化指针
int count;
public:
void log() {
for(int i=0; i<count; ++i) // 访问野指针
cout << readings[i] << endl;
}
};
现代C++的修正方案:
cpp复制class SensorData {
int* readings = nullptr; // 类内初始值
int count = 0;
public:
SensorData() = default; // 显式声明需要默认构造
};
2.2 委托构造的链式调用
C++11引入的委托构造允许构造函数调用同类其他构造函数:
cpp复制class NetworkConnection {
string address;
int port;
b
