问题的历史:
C++是在C的基础上构建的,C提供了一小部分内置类型:数字、字符和数组。随着语言的发展,出现了结构、类和枚举等概念,它们便成为了用户定义类型。
问题:
程序中的数据类型决定了变量所占用的内存量、它如何被初始化、复制、销毁和比较。内置类型具有标准定义的行为,而用户定义类型则需要明确描述所有方面。管理用户定义类型的错误可能导致崩溃、内存泄漏、不公平的对象比较等。
解决方案:
内置类型包括int、float、double、char、bool等其他“原始”类型。用户定义类型是您创建的任何结构、类和联合。对于复杂的任务,需要引入具有关联的复制、比较和资源管理语义的用户定义类型。
代码示例:
// 内置类型 int x = 5; // 用户定义类型 struct Point { double x, y; }; Point a = {1.0, 2.0}; // 带有资源的类 class MyFile { public: MyFile(const std::string& fn) : f(fopen(fn.c_str(), "r")) {} ~MyFile() { if (f) fclose(f); } private: FILE* f; };
用户定义类型是否可以完全像内置类型一样表现(例如,默认通过==进行比较)?
不可以,==的比较仅从C++20开始默认通过defaulted operator==工作,在此之前需要明确定义。
如果类只持有原始指针(int、FILE等),可以不实现复制构造函数和析构函数吗?**
不可以,在这种情况下,默认的复制将是“浅复制”,这将导致内存/资源泄漏或双重释放。需要实现“五法则”。
结构的默认值是否会被初始化为零(Point p;)?
不会,局部结构可能未被初始化,内存内容是随机的。请显式使用初始化。
负面案例:
一个文件的包装类没有实现析构函数。当处理成千上万的文件而未关闭描述符时,程序一直在工作。
优点:代码更少,外观简化 缺点:资源泄漏,不稳定的行为
正面案例:
一个类实现了RAII——在构造函数中打开文件,在析构函数中关闭,禁止复制。
优点:可靠、安全、干净的接口 缺点:需要记住“五法则”,需要编写更多代码