面试题答案
一键面试重写拷贝构造函数的必要性
- 深拷贝需求:
std::vector<std::list<int>>
本身是值语义的,其拷贝构造函数会进行值拷贝,即每个std::list<int>
会被完整地复制一份。然而,当Graph
类包含自定义的GraphProperties
类成员变量时,如果不重写拷贝构造函数,默认的拷贝构造函数将对GraphProperties
进行浅拷贝。这可能导致多个Graph
对象共享同一份GraphProperties
数据,在某些情况下(如GraphProperties
包含动态分配的资源)会引发内存管理问题,如释放多次或悬空指针。
- 保持对象独立性:
- 对于图结构,每个
Graph
对象应该是独立的,拥有自己独立的邻接表和图属性。重写拷贝构造函数可以确保在复制Graph
对象时,新对象与原对象完全独立,修改新对象的属性或结构不会影响原对象,反之亦然。
- 对于图结构,每个
重写时需要注意的要点
- 成员变量深拷贝:
- 对于
std::vector<std::list<int>>
邻接表,由于其默认拷贝构造函数是深拷贝,无需额外操作。但对于GraphProperties
成员变量,需要确保进行深拷贝。如果GraphProperties
类没有自定义的拷贝构造函数,可能需要为其添加,以保证GraphProperties
内部的资源(如动态分配的内存)也被正确复制。
- 对于
- 异常安全性:
- 在重写拷贝构造函数时,要确保异常安全性。即如果在拷贝过程中抛出异常,原对象和目标对象都应处于有效状态。例如,在复制
GraphProperties
时,如果抛出异常,邻接表的复制应该回滚(如果部分已经复制),以避免资源泄漏或对象处于不一致状态。
- 在重写拷贝构造函数时,要确保异常安全性。即如果在拷贝过程中抛出异常,原对象和目标对象都应处于有效状态。例如,在复制
- 性能考虑:
- 虽然深拷贝通常会带来一定的性能开销,但应尽量优化。例如,可以考虑使用移动语义(如果
GraphProperties
支持)在合适的情况下提高性能,避免不必要的重复分配和复制操作。
- 虽然深拷贝通常会带来一定的性能开销,但应尽量优化。例如,可以考虑使用移动语义(如果
实现步骤
- 定义拷贝构造函数:
class Graph { private: std::vector<std::list<int>> adjList; GraphProperties properties; public: // 拷贝构造函数 Graph(const Graph& other) : adjList(other.adjList) { // 深拷贝GraphProperties properties = other.properties; // 如果GraphProperties没有正确的赋值运算符重载实现深拷贝,这里需要手动实现 // 例如,如果GraphProperties包含一个动态分配的指针成员变量data // properties.data = new SomeType(*other.properties.data); } };
- 验证
GraphProperties
拷贝:- 确保
GraphProperties
类有正确的拷贝构造函数或赋值运算符重载,以保证其内部资源的深拷贝。如果GraphProperties
类没有合适的实现,需要在GraphProperties
类中添加相应的深拷贝逻辑。
- 确保
- 测试异常安全性:
- 在实现拷贝构造函数后,编写测试用例验证异常安全性。例如,可以在
GraphProperties
的拷贝构造函数或赋值运算符中故意抛出异常,检查Graph
的拷贝构造函数是否能正确处理,确保原对象和目标对象都处于有效状态。
- 在实现拷贝构造函数后,编写测试用例验证异常安全性。例如,可以在