C++中构造函数和析构函数


在C++面试中,构造函数和析构函数是高频考点。以下是常见的面试题及其解析,帮助你系统掌握相关知识点:


一、基础概念题

1. 什么是构造函数和析构函数?它们的作用是什么?

  • 构造函数:在对象创建时自动调用,用于初始化对象成员。
  • 析构函数:在对象销毁时自动调用,用于释放资源(如内存、文件句柄等)。
  • 关键区别:构造函数可重载,析构函数不能重载且无参数。

2. 如果没有显式定义构造函数和析构函数,会发生什么?

  • 编译器会生成默认的无参构造函数和析构函数,但:
    • 默认构造函数不会初始化内置类型(如 intfloat)。
    • 默认析构函数不会释放动态分配的资源(如 new 分配的内存)。

二、构造函数相关题

3. 什么是拷贝构造函数?什么时候会被调用?

  • 定义:用同类的另一个对象初始化新对象。
    class MyClass {
    public:
        MyClass(const MyClass& other) { /* 拷贝逻辑 */ }
    };
    
  • 调用场景
    • 对象直接初始化:MyClass obj2 = obj1;
    • 函数传参(按值传递):void func(MyClass obj)
    • 函数返回对象(按值返回时可能触发,取决于编译器优化)。

4. 什么是移动构造函数(C++11)?它与拷贝构造函数的区别?

  • 移动构造函数:通过“窃取”临时对象(右值)的资源来初始化新对象,避免深拷贝。
    MyClass(MyClass&& other) noexcept { 
        ptr = other.ptr;  // 转移资源
        other.ptr = nullptr; 
    }
    
  • 区别
    • 拷贝构造函数保留原对象,移动构造函数使原对象处于有效但未定义状态。

5. 构造函数能否是虚函数?为什么?

  • 不能:构造函数调用时对象尚未完全创建,虚函数表(vtable)未初始化,无法实现多态。

6. 构造函数抛出异常会导致什么问题?如何解决?

  • 问题:对象构造不完整,但析构函数不会被调用,可能导致资源泄漏。
  • 解决
    • 使用智能指针(如 std::unique_ptr)管理资源。
    • 在构造函数内捕获异常并清理已分配的资源。

三、析构函数相关题

7. 析构函数能否抛出异常?为什么?

  • 不建议:如果析构函数抛出异常且未被捕获,程序可能直接终止(std::terminate)。
  • 最佳实践:在析构函数中处理所有异常,避免抛出。

8. 为什么基类的析构函数需要声明为虚函数?

  • 原因:若派生类对象通过基类指针删除,非虚析构函数会导致派生类的析构函数未被调用,引发资源泄漏。
  • 示例
    class Base {
    public:
        virtual ~Base() {} // 虚析构函数
    };
    class Derived : public Base {
        ~Derived() { /* 清理派生类资源 */ }
    };
    Base* ptr = new Derived();
    delete ptr; // 正确调用派生类析构函数
    

9. 什么是“虚析构函数”的必要性?举例说明。

  • 场景:多态基类中,若未定义虚析构函数,通过基类指针删除派生类对象会导致未定义行为。
  • 错误示例
    class Base { ~Base() {} }; // 非虚析构函数
    delete new Derived();      // 派生类资源泄漏!
    

四、综合应用题

10. 以下代码的输出是什么?分析原因。

class Test {
public:
    Test() { cout << "Constructor\n"; }
    ~Test() { cout << "Destructor\n"; }
};
void func() { Test t; }
int main() { func(); }
  • 输出
    Constructor
    Destructor
    
  • 解析t 是局部对象,函数结束时析构。

11. 以下代码有什么问题?如何修正?

class Resource {
    int* data;
public:
    Resource() { data = new int[100]; }
    ~Resource() { delete data; } // 错误!
};
  • 问题new[] 应用 delete[] 释放,否则行为未定义。
  • 修正
    ~Resource() { delete[] data; }
    

12. 实现一个单例类,要求线程安全(C++11以后)。

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} // 私有构造函数
public:
    Singleton(const Singleton&) = delete; // 禁用拷贝
    static Singleton* getInstance() {
        static Singleton inst; // C++11保证线程安全
        return &inst;
    }
};

五、高级问题

13. 什么是RAII?举例说明其在构造函数/析构函数中的应用。

  • RAII(Resource Acquisition Is Initialization):资源在构造函数中获取,在析构函数中释放。
  • 示例std::fstream 在析构时自动关闭文件。

14. 如何防止对象被拷贝或赋值?

  • C++11:
    class NonCopyable {
    public:
        NonCopyable(const NonCopyable&) = delete;
        NonCopyable& operator=(const NonCopyable&) = delete;
    };
    

15. 解释“三/五法则”(Rule of Three/Five)。

  • Rule of Three:若需自定义析构函数、拷贝构造函数或拷贝赋值运算符,通常需要同时定义这三者。
  • Rule of Five(C++11):增加移动构造函数和移动赋值运算符。

总结

  • 构造函数:关注重载、默认/拷贝/移动构造、初始化列表。
  • 析构函数:重点在虚析构函数、资源释放、异常安全。
  • 高频考点:RAII、Rule of Three/Five、深拷贝 vs 浅拷贝。
posted @ 2025-03-26 15:33  guanyubo  阅读(318)  评论(0)    收藏  举报