请看如下一段代码:
#include<iostream>
using namespace std;
class A
{
public:
A()
{
pValue = new int[100];
cout << "Constructor of A" << endl;
}
~A()
{
delete []pValue;
cout << "Deconstructor of A " << endl;
}
private:
int *pValue;
};
class B
{
public:
B()
{
cout << "Constructor of B " << endl;
}
virtual ~B()
{
cout << "Deconstructor of B" << endl;
}
};
class C : public B
{
public:
C()
{
cout << "Constructor of C " << endl;
}
virtual ~C()
{
cout << "Deconstructor of C" << endl;
}
private:
A a;
};
int main(void)
{
C c; //构造函数在栈中分配
// B* b = new C;
// delete b;
return 0;
}
类C从类B中继承出来,类C中聚合了一个类A的对象,类C的析构函数并未调用类A的析构函数,请问:这段代码执行后,会调用类A的析构函数释放内存吗?经测试,执行结果如下:
Constructor of B
Constructor of A
Constructor of C
Deconstructor of C
Deconstructor of A
Deconstructor of B
由此可以看出,C的析构函数确实调用了类A的析构函数,该调用是由编译器隐含加入的,随后还调用了基类B的析构函数,但是请注意,编译器只会为基类和聚合类添加对析构函数的调用,如果C中的成员为指向类A的指针,则编译器不会加入对类A的析构函数的调用。
那么,如果我们把main函数改变一下:
int main(int argc, char* argv[])
{
B * b = new C; //构造函数在堆中分配
delete b;
return 0;
}
请问:这段代码会发生内存泄漏吗?执行结果如下:
Constructor of B
Constructor of A
Constructor of C
Deconstructor of B
发生了什么?调用了A的构成函数分配了内存,却只调用了B的析构函数,C和A的析构函数都没有调用,内存没有释放,为什么?
有人也许已经看出了问题所在,B和C的析构函数都应该是虚函数,否则,由于b的类型为指向类B的指针,delete b只会调用B的析构函数。当B和C的析构函数都是虚函数时,编译器会根据b指向的对象实际上是C的对象,而去调用C的析构函数。请对代码作如下改变:
class B {
public:
virtual ~B () { printf("Deconstructor of B\n"); }
//所有B的派生类的析构函数都将自动变为virtual型
};
class C : public B {
public:
virtual ~C () { printf("Deconstructor of C\n"); } // 此处virtual可有可无
private:
A a;
};
再次运行,结果正确:
Constructor of B
Constructor of A
Constructor of C
Deconstructor of C
Deconstructor of A
Deconstructor of B
分享到:
相关推荐
### C#编程艺术:构造函数与析构函数的奥秘 C#作为一种强大的面向对象的编程语言,在软件开发领域占据着举足轻重的地位。它不仅受到C++和Java的影响,还结合了现代编程语言的优点,如垃圾回收、类型安全、泛型支持...
C/C++项目开发是指使用C或C++这两种编程语言来设计和实现软件项目的过程。C和C++都是非常流行和强大的编程语言,广泛应用于系统编程、嵌入式开发、游戏开发、桌面应用程序、高性能服务器和客户端应用程序等多个领域...
3. **构造与析构函数**:构造函数用于初始化对象,析构函数则在对象生命周期结束时执行清理工作。了解它们的工作原理和使用时机可以避免内存管理和资源泄露的问题。 4. **继承与多态**:继承允许子类从父类中继承...
3. **构造函数与析构函数**:构造函数用于初始化类的对象,析构函数则在对象生命周期结束时执行,用于清理资源。理解这两者的用途和编写方式对于内存管理至关重要。 4. **运算符重载**:C++允许我们为已有的运算符...
4. **构造函数与析构函数**:构造函数用于初始化新创建的对象,而析构函数则在对象生命周期结束时自动调用,负责清理工作。 5. **模板**:C++的模板功能允许我们编写泛型代码,可以用于创建泛型函数和泛型类,提高...
9. RAII(Resource Acquisition Is Initialization):这是一种资源管理策略,通过构造函数获取资源,并在析构函数中释放资源。RAII是C++中实现智能指针和其他自动资源管理的关键。 10. C++11及以后的更新:C++11...
6. **构造函数与析构函数**:构造函数在对象创建时自动调用,用于初始化对象的状态;析构函数则在对象生命周期结束时执行,负责清理对象占用的资源。了解这两者的工作机制是理解C++对象生命周期的关键。 7. **...
5. **构造函数与析构函数**: 构造函数是在对象创建时自动调用的特殊函数,用于初始化对象的成员。析构函数则在对象生命周期结束时被调用,用于清理资源。这些小程序会展示如何定义和使用这两种函数。 6. **继承与...
9. **类的构造函数、析构函数与赋值函数**:分析了构造函数与析构函数的起源、初始化列表、构造和析构的顺序、拷贝构造函数与赋值函数的实现,以及派生类中类基本函数的实现。 10. **类的继承与组合**:讨论了继承...
书中阐述了构造函数的语义,包括何时调用构造函数,构造函数如何与其他成员函数(如析构函数、拷贝构造函数等)协同工作,以及编译器如何为未显式定义的构造函数提供默认实现。 #### 1.3 数据成员的语义 数据成员...
TObject提供了对象的基本特性,如构造函数、析构函数以及继承机制;TPersistent负责对象的状态保存和恢复;TComponent则是所有可视化和非可视化组件的基类,它提供了组件的属性、事件和容器管理功能。 除了VCL和FCL...
同时,深入理解构造函数、析构函数、成员函数和友元函数的作用。 3. **内存管理**:C++支持动态内存分配,理解指针的操作至关重要。学会new和delete操作,以及如何避免内存泄漏。 4. **继承与多态**:理解类的继承...
理解类的构造函数、析构函数、封装、继承、多态等概念,有助于构建复杂的系统架构。 3. **泛型**:C#的泛型提供了一种方式,允许我们创建可以处理多种数据类型的代码,这样可以提高代码的重用性和安全性。通过泛型...
同时,理解构造函数、析构函数、拷贝构造函数以及赋值运算符重载对于正确管理和初始化对象至关重要。 继承、多态和抽象是C++面向对象三大特性。继承允许一个类(子类)从另一个类(父类)继承属性和行为,增强了...
类是OOP的基础,通过构造函数初始化对象,通过析构函数清理资源。继承允许子类继承父类的属性和方法,实现代码重用。多态则通过接口和抽象类实现,增强了代码的灵活性和可扩展性。 在.NET框架中,C#与组件模型...
- **析构函数**:用于释放非托管资源,如文件句柄或数据库连接等,在对象生命周期结束前自动调用。 #### 多态 - **多态**:多态允许子类重写父类的方法,从而使同一个方法可以表现出不同的行为。 - **多态实现与...