C++学习笔记-虚析构函数

在C++中,如果基类有虚函数,那么一定要将析构函数标记为virtual,确保能通过基类指针删除派生类对象时,能够正确调用派生类的析构函数,从而避免资源泄漏或未定义行为。

虚析构函数的必要性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

class Base
{
public:
    Base() { std::cout << "Base Constructor\n"; }
    virtual ~Base() { std::cout << "Base Destructor\n"; }
};

class Derived : public Base
{
public:
    Derived() { std::cout << "Derived Constructor\n"; }
    ~Derived() { std::cout << "Derived Destructor\n"; }
};

int main()
{
    std::cout << "=== Creating and deleting Base object ===" << std::endl;
    Base* base = new Base();
    delete base;

    std::cout << "\n=== Creating and deleting Derived object ===" << std::endl;
    Derived* derived = new Derived();
    delete derived;

    std::cout << "\n=== Polymorphic deletion (with virtual destructor) ===" << std::endl;
    /* 删除Base中的virtual关键字看看会发生什么 */
    Base* poly = new Derived();
    delete poly; // 在多态情况下,只会调用Base的析构函数,而不会调用Derived的析构函数

    return 0;
}

没有虚析构函数的问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>

class NonVirtualBase
{
public:
    NonVirtualBase() { std::cout << "NonVirtualBase Constructor\n"; }
    ~NonVirtualBase() { std::cout << "NonVirtualBase Destructor\n"; }  // 注意:没有virtual
};

class NonVirtualDerived : public NonVirtualBase
{
private:
    int* m_Data;
    
public:
    NonVirtualDerived() 
    {
        std::cout << "NonVirtualDerived Constructor\n";
        m_Data = new int[100];  // 分配内存
        std::cout << "Allocated memory in Derived\n";
    }
    
    ~NonVirtualDerived() 
    {
        std::cout << "NonVirtualDerived Destructor\n";
        delete[] m_Data;  // 释放内存
        std::cout << "Freed memory in Derived\n";
    }
};

class VirtualBase
{
public:
    VirtualBase() { std::cout << "VirtualBase Constructor\n"; }
    virtual ~VirtualBase() { std::cout << "VirtualBase Destructor\n"; }  // virtual析构函数
};

class VirtualDerived : public VirtualBase
{
private:
    int* m_Data;
    
public:
    VirtualDerived() 
    {
        std::cout << "VirtualDerived Constructor\n";
        m_Data = new int[100];  // 分配内存
        std::cout << "Allocated memory in VirtualDerived\n";
    }
    
    ~VirtualDerived() 
    {
        std::cout << "VirtualDerived Destructor\n";
        delete[] m_Data;  // 释放内存
        std::cout << "Freed memory in VirtualDerived\n";
    }
};

void DemonstrateDestructorProblem()
{
    std::cout << "=== Problem: Non-virtual destructor ===" << std::endl;
    NonVirtualBase* obj1 = new NonVirtualDerived();
    delete obj1;  // 只调用基类析构函数,内存泄漏!
    
    std::cout << "\n=== Solution: Virtual destructor ===" << std::endl;
    VirtualBase* obj2 = new VirtualDerived();
    delete obj2;  // 正确调用派生类和基类析构函数
}

int main()
{
    DemonstrateDestructorProblem();
    return 0;
}

实际应用场景

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <vector>
#include <memory>

// 抽象基类
class Shape
{
protected:
    std::string m_Name;
    
public:
    Shape(const std::string& name) : m_Name(name) 
    {
        std::cout << "Creating " << m_Name << std::endl;
    }
    
    virtual ~Shape() 
    {
        std::cout << "Destroying " << m_Name << std::endl;
    }
    
    virtual void Draw() const = 0;
    virtual double GetArea() const = 0;
    
    const std::string& GetName() const { return m_Name; }
};

class Circle : public Shape
{
private:
    double m_Radius;
    double* m_CachedArea;  // 模拟需要清理的资源
    
public:
    Circle(double radius) : Shape("Circle"), m_Radius(radius)
    {
        m_CachedArea = new double(3.14159 * radius * radius);
        std::cout << "Circle allocated cache memory" << std::endl;
    }
    
    ~Circle()
    {
        delete m_CachedArea;
        std::cout << "Circle freed cache memory" << std::endl;
    }
    
    void Draw() const override
    {
        std::cout << "Drawing circle with radius " << m_Radius << std::endl;
    }
    
    double GetArea() const override
    {
        return *m_CachedArea;
    }
};

class Rectangle : public Shape
{
private:
    double m_Width, m_Height;
    std::vector<int>* m_PixelData;  // 模拟需要清理的资源
    
public:
    Rectangle(double width, double height) : Shape("Rectangle"), m_Width(width), m_Height(height)
    {
        m_PixelData = new std::vector<int>(1000, 0);  // 模拟像素数据
        std::cout << "Rectangle allocated pixel data" << std::endl;
    }
    
    ~Rectangle()
    {
        delete m_PixelData;
        std::cout << "Rectangle freed pixel data" << std::endl;
    }
    
    void Draw() const override
    {
        std::cout << "Drawing rectangle " << m_Width << "x" << m_Height << std::endl;
    }
    
    double GetArea() const override
    {
        return m_Width * m_Height;
    }
};

class Triangle : public Shape
{
private:
    double m_Base, m_Height;
    
public:
    Triangle(double base, double height) : Shape("Triangle"), m_Base(base), m_Height(height) {}
    
    ~Triangle()
    {
        std::cout << "Triangle destructor called" << std::endl;
    }
    
    void Draw() const override
    {
        std::cout << "Drawing triangle with base " << m_Base << " and height " << m_Height << std::endl;
    }
    
    double GetArea() const override
    {
        return 0.5 * m_Base * m_Height;
    }
};

void ShapeDemo()
{
    std::cout << "=== Shape Polymorphism Demo ===" << std::endl;
    
    std::vector<Shape*> shapes;
    
    shapes.push_back(new Circle(5.0));
    shapes.push_back(new Rectangle(4.0, 6.0));
    shapes.push_back(new Triangle(3.0, 8.0));
    
    std::cout << "\nDrawing all shapes:" << std::endl;
    for (const auto& shape : shapes)
    {
        shape->Draw();
        std::cout << "Area: " << shape->GetArea() << std::endl;
    }
    
    std::cout << "\nDeleting all shapes:" << std::endl;
    for (auto& shape : shapes)
    {
        delete shape;  // 虚析构函数确保正确清理
    }
    
    shapes.clear();
}

int main()
{
    ShapeDemo();
    return 0;
}

智能指针与虚析构函数

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <iostream>
#include <memory>
#include <vector>

class Resource
{
private:
    std::string m_Name;
    int* m_Data;
    
public:
    Resource(const std::string& name) : m_Name(name)
    {
        m_Data = new int[1000];
        std::cout << "Resource " << m_Name << " allocated" << std::endl;
    }
    
    virtual ~Resource()
    {
        delete[] m_Data;
        std::cout << "Resource " << m_Name << " deallocated" << std::endl;
    }
    
    virtual void Use() const
    {
        std::cout << "Using resource " << m_Name << std::endl;
    }
    
    const std::string& GetName() const { return m_Name; }
};

class FileResource : public Resource
{
private:
    FILE* m_File;
    
public:
    FileResource(const std::string& filename) : Resource("File: " + filename)
    {
        m_File = fopen("temp.txt", "w");
        if (m_File)
        {
            std::cout << "File " << filename << " opened" << std::endl;
        }
    }
    
    ~FileResource()
    {
        if (m_File)
        {
            fclose(m_File);
            std::cout << "File closed" << std::endl;
        }
    }
    
    void Use() const override
    {
        std::cout << "Writing to file resource" << std::endl;
        if (m_File)
        {
            fprintf(m_File, "Hello from FileResource\n");
            fflush(m_File);
        }
    }
};

class NetworkResource : public Resource
{
private:
    int m_Socket;
    
public:
    NetworkResource(const std::string& address) : Resource("Network: " + address)
    {
        m_Socket = 12345;  // 模拟socket
        std::cout << "Network connection to " << address << " established" << std::endl;
    }
    
    ~NetworkResource()
    {
        std::cout << "Network connection closed (socket " << m_Socket << ")" << std::endl;
    }
    
    void Use() const override
    {
        std::cout << "Sending data through network resource" << std::endl;
    }
};

void SmartPointerDemo()
{
    std::cout << "=== Smart Pointer with Virtual Destructor Demo ===" << std::endl;
    
    std::vector<std::unique_ptr<Resource>> resources;
    
    resources.push_back(std::make_unique<FileResource>("data.txt"));
    resources.push_back(std::make_unique<NetworkResource>("192.168.1.1"));
    resources.push_back(std::make_unique<Resource>("Generic"));
    
    std::cout << "\nUsing all resources:" << std::endl;
    for (const auto& resource : resources)
    {
        resource->Use();
    }
    
    std::cout << "\nResources will be automatically cleaned up when going out of scope..." << std::endl;
    // unique_ptr会自动调用正确的析构函数
}

void SharedPointerDemo()
{
    std::cout << "\n=== Shared Pointer Demo ===" << std::endl;
    
    std::shared_ptr<Resource> resource1 = std::make_shared<FileResource>("shared.txt");
    
    {
        std::shared_ptr<Resource> resource2 = resource1;  // 共享所有权
        std::cout << "Reference count: " << resource1.use_count() << std::endl;
        resource2->Use();
    }  // resource2超出作用域,但对象不会被销毁
    
    std::cout << "Reference count after inner scope: " << resource1.use_count() << std::endl;
    resource1->Use();
    
    std::cout << "Setting resource1 to nullptr..." << std::endl;
    resource1 = nullptr;  // 现在对象会被销毁
}

int main()
{
    SmartPointerDemo();
    SharedPointerDemo();
    return 0;
}

抽象基类的最佳实践

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <iostream>
#include <memory>

// 抽象接口类
class IDrawable
{
public:
    virtual ~IDrawable() = default;  // 默认虚析构函数
    virtual void Draw() const = 0;
};

class ISerializable
{
public:
    virtual ~ISerializable() = default;
    virtual std::string Serialize() const = 0;
    virtual void Deserialize(const std::string& data) = 0;
};

// 具体实现类
class Button : public IDrawable, public ISerializable
{
private:
    std::string m_Text;
    int m_Width, m_Height;
    
public:
    Button(const std::string& text, int width, int height)
        : m_Text(text), m_Width(width), m_Height(height)
    {
        std::cout << "Button created: " << m_Text << std::endl;
    }
    
    ~Button()
    {
        std::cout << "Button destroyed: " << m_Text << std::endl;
    }
    
    void Draw() const override
    {
        std::cout << "Drawing button: " << m_Text 
                  << " (" << m_Width << "x" << m_Height << ")" << std::endl;
    }
    
    std::string Serialize() const override
    {
        return "Button{text:" + m_Text + ",width:" + std::to_string(m_Width) 
               + ",height:" + std::to_string(m_Height) + "}";
    }
    
    void Deserialize(const std::string& data) override
    {
        std::cout << "Deserializing button from: " << data << std::endl;
        // 简化的反序列化逻辑
    }
};

class Label : public IDrawable, public ISerializable
{
private:
    std::string m_Text;
    std::string m_Font;
    
public:
    Label(const std::string& text, const std::string& font)
        : m_Text(text), m_Font(font)
    {
        std::cout << "Label created: " << m_Text << std::endl;
    }
    
    ~Label()
    {
        std::cout << "Label destroyed: " << m_Text << std::endl;
    }
    
    void Draw() const override
    {
        std::cout << "Drawing label: " << m_Text << " (font: " << m_Font << ")" << std::endl;
    }
    
    std::string Serialize() const override
    {
        return "Label{text:" + m_Text + ",font:" + m_Font + "}";
    }
    
    void Deserialize(const std::string& data) override
    {
        std::cout << "Deserializing label from: " << data << std::endl;
    }
};

// 工厂函数
std::unique_ptr<IDrawable> CreateWidget(const std::string& type)
{
    if (type == "button")
        return std::make_unique<Button>("Click Me", 100, 30);
    else if (type == "label")
        return std::make_unique<Label>("Hello World", "Arial");
    else
        return nullptr;
}

void InterfaceDemo()
{
    std::cout << "=== Interface Demo ===" << std::endl;
    
    auto button = CreateWidget("button");
    auto label = CreateWidget("label");
    
    if (button)
    {
        button->Draw();
        
        // 尝试转换为ISerializable
        if (auto serializable = dynamic_cast<ISerializable*>(button.get()))
        {
            std::cout << "Serialized: " << serializable->Serialize() << std::endl;
        }
    }
    
    if (label)
    {
        label->Draw();
        
        if (auto serializable = dynamic_cast<ISerializable*>(label.get()))
        {
            std::cout << "Serialized: " << serializable->Serialize() << std::endl;
        }
    }
    
    std::cout << "Widgets will be automatically destroyed..." << std::endl;
}

int main()
{
    InterfaceDemo();
    return 0;
}

虚析构函数的性能考虑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <chrono>
#include <vector>

class NonVirtualClass
{
private:
    int m_Value;
    
public:
    NonVirtualClass(int value) : m_Value(value) {}
    ~NonVirtualClass() {}  // 非虚析构函数
    
    int GetValue() const { return m_Value; }
};

class VirtualClass
{
private:
    int m_Value;
    
public:
    VirtualClass(int value) : m_Value(value) {}
    virtual ~VirtualClass() {}  // 虚析构函数
    
    int GetValue() const { return m_Value; }
};

template<typename T>
void PerformanceTest(const std::string& className)
{
    const int count = 1000000;
    
    auto start = std::chrono::high_resolution_clock::now();
    
    std::vector<T*> objects;
    objects.reserve(count);
    
    // 创建对象
    for (int i = 0; i < count; ++i)
    {
        objects.push_back(new T(i));
    }
    
    // 删除对象
    for (auto* obj : objects)
    {
        delete obj;
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << className << " test: " << duration.count() << " ms" << std::endl;
}

void PerformanceComparison()
{
    std::cout << "=== Performance Comparison ===" << std::endl;
    std::cout << "Creating and deleting 1,000,000 objects..." << std::endl;
    
    PerformanceTest<NonVirtualClass>("NonVirtual");
    PerformanceTest<VirtualClass>("Virtual");
    
    std::cout << "\nNote: Virtual destructors have minimal overhead" << std::endl;
    std::cout << "The benefits far outweigh the small performance cost" << std::endl;
}

int main()
{
    PerformanceComparison();
    return 0;
}

总结

  1. 虚析构函数的必要性:当基类有虚函数时,析构函数必须是虚函数
  2. 问题根源
    • 通过基类指针删除派生类对象
    • 非虚析构函数只调用基类析构函数
    • 导致派生类资源无法正确释放
  3. 解决方案
    • 在基类中声明虚析构函数:virtual ~Base() {}
    • 或使用默认虚析构函数:virtual ~Base() = default;
  4. 最佳实践
    • 任何作为基类的类都应该有虚析构函数
    • 抽象基类使用纯虚析构函数或默认虚析构函数
    • 使用智能指针自动管理内存
  5. 性能影响
    • 虚析构函数有轻微的性能开销
    • 但相比内存泄漏的风险,这个开销是值得的
  6. 现代C++
    • 优先使用智能指针
    • 使用= default简化代码
    • 遵循RAII原则
updatedupdated2025-09-202025-09-20