C++进阶剖析(二十三)之 问题解析四

本文围绕C++编程展开,探讨了三个问题。一是new和malloc在内存分配及初始化上的区别,以及delete和free的差异;二是构造函数不能为虚函数,析构函数可以且建议设为虚函数,还分析了相关案例;三是继承中dynamic_cast强制类型转换的使用及示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 问题一
1.1.1 new 和malloc 的区别

  • new 关键字是C++的一部分
  • malloc是由C库提供的函数(如果没有C库就不能申请内存空间)
  • new 是以具体类型为单位进程内存分配
  • malloc 以字节为单位进程内存分配
  • new 在申请内存空间的时候可进行初始化
  • malloc 仅仅根据需要申请定量的内存空间

1.1.2 实例代码

#include <iostream>
using namespace std;
class Test
{
public:
	Test()
	{
		cout<<"Test::Test()"<<endl;
	}
};
int main()
{
	Test * p1= new Test;
	Test * p2 =(Test*) malloc(sizeof(Test));
	return 0;
 }

运行结果
在这里插入图片描述
通过运行结果可以看出,构造函数仅仅被调用了一次,说明是new进行了初始化,malloc仅仅是分配了空间,并不进行初始化。

1.1.3 delete 和free 的区别

using namespace std;

class Test
{
    int* mp;
public:
    Test()
    {
        cout << "Test::Test()" << endl;
        
        mp = new int(100);
        
        cout << *mp << endl;
    }
    ~Test()
    {
        delete mp;
        cout << "~Test::Test()" << endl;
    }
};

int main()
{
    Test* pn = new Test;
    Test* pm = (Test*)malloc(sizeof(Test));
    
    free( pn);  //虽然可以释放new 申请的内存,但是不会调用析构函数,不会释放mp 申请的资源,导致内存泄漏
    delete(pm);  //pm指向的并不是一个合法的对象,调用delete可能会导致错误的产生
    
    return 0;
}

1.2 问题二

1.2.1 构造函数是否可以成为虚函数?

  • 构造函数不可能成为虚函数,构造函数执行结束以后,虚函数表指针才被正确的初始化

1.2.2 析构函数是否可以成为虚函数?
析构函数可以成为虚函数,因为析构函数是在对象销毁前调用的,虚函数表指针是正确的指向虚函数表的,建议设计类时将析构函数声明为虚函数。

1.2.3案例分析
//为什么建议将析构函数声明为虚函数。

#include <iostream>
using namespace std;

class Parent
{
public:
	Parent()
	{
		cout<<"Parent()"<<endl;
	}
	~Parent()
	{
		cout<<"~Parent()"<<endl;
	}

};
class Child:public Parent
{
public:
	Child()
	{
		cout<<"Child()"<<endl;
	}
	~Child()
	{
		cout<<"Child()"<<endl;
	}
};

int main()
{
	Parent * p = new Child;
	
	delete p;
	return 0;
 }

运行结果:
在这里插入图片描述
分析结果:在main函数中父类指针指向了子类对象,在销毁对象的时候,我们期望销毁的是子类对象,但是从结果来看,编译器销毁的是父类对象,那该怎么办?将析构函数声明为虚函数就可以了。所以建议在编程的时候将析构函数声明为虚函数。

1.2.4
构造函数中(内部)不可能发生多态,因为在构造函数执行的时候,虚函数表指针未被正确初始化
析构函数中((内部))不可能发生多态行为,在析构函数执行的时候,虚函数表指针已经被销毁。
构造函数中调用的虚函数必定是当前类中定义的版本

  • 构造函数和析构函数中不能发生多态行为,只调用当前类中定义的函数版本。

1.3 问题三
1.3.1 继承中如何使用强制类型转换

  • dynamic_cast 是与继承相关的类型转换关键字
  • dynamic_cast要求相关的类中必须有虚函数
  • 用于有直接或间接继承关系的指针(引用)之间
    (1)指针: 转换成功:得到目标类型的指针,转换失败:得到一个空指针
    (2) 引用:转换成功:得到目标类型的引用,转换失败:得到一个异常的操作信息。

1.3.2 示例

class Parent
{
public:
	Parent()
	{
		cout<<"Parent()"<<endl;
	}
	virtual ~Parent()
	{
		cout<<"~Parent()"<<endl;
	}
};

class Child : public Parent
{

};

int main()
{
	Parent * p = new Child;
	Child* c = dynamic_cast<Child*>(p);
	if(c != NULL)
	{
		cout<<"c =" <<c<<endl;
	}
	else
	{
		cout <<"cast error" <<endl;
	}
	cout<<endl;
	Parent * p2 = new Parent;
	Child* c2 = dynamic_cast<Child*>(p2);
	if(c2 != NULL)
	{
		cout<<"c =" <<c2<<endl;
	}
	else
	{
		cout <<"cast error" <<endl;
	}
	return 0;
}
  • 结果
    在这里插入图片描述

参考一 :狄泰软件学院C++进阶剖析
参考二 : C++ primer中文版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值