VerySource

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
楼主: 心碎飞了

关于virtual的析构函数

[复制链接]

0

主题

49

帖子

34.00

积分

新手上路

Rank: 1

积分
34.00
发表于 2021-3-8 21:30:01 | 显示全部楼层
这跟析构函数能不能继承没有关系,对于
Base *p = new Derive;
p的静态类型是Base*,而动态类型则是Derive*,若不声明virtual析构函数,则会调用其静态类型的析构函数,即Base类的析构函数。另外,析构函数的调用方式,先Drive类的析构函数,然后是Base类的析构函数,这个有没有virtual都是一样的,而若没有virtual,delete p;就少调用了一个析构函数了!
回复

使用道具 举报

1

主题

39

帖子

27.00

积分

新手上路

Rank: 1

积分
27.00
发表于 2021-3-8 21:45:01 | 显示全部楼层

析构函数可以被继承吧.是构造函数不能被继承吧(或许也不是,只是不能被定义为虚函数).

为何析构函数要弄成虚函数, 就是为了析构顺序的正确性.
构造的时候是从基类开始到子类.
析构的顺序就刚好相反.
构造的时候我们能够清楚调用正确的函数,但是析构的时候如果是被基类的指针所指向.
没有虚函数的机制,那么就是调用基类的析构函数.
基类不可能知道子类有什么新申请的资源没有释放吧.
因而必需借助虚函数的机制来做到这点.通过准确调用子类的析构函数,那么就
能释放资源了.

回复

使用道具 举报

0

主题

3

帖子

4.00

积分

新手上路

Rank: 1

积分
4.00
发表于 2021-3-8 22:00:01 | 显示全部楼层
没看懂楼主的话,“为了让子类继承”这个描述很难理解。“继承”的概念是什么?这个要先作好定义。

virtual成员函数可以(或者说“是为了”)被其衍生类override。析构函数可以被override。析构函数的实现自动并且强制调用其基类的实现。

virtual的含义是“调用时根据对象决定其实际执行的代码”,与是不是析构函数无关。

只要是非静态成员,调用时该类已经构造完成并且没有被析构,就可以是virtual的。构造函数符合这个条件(可能其衍生类部分已经被析构,但它所属的这个类的部分一定还没开始析构)。

回复

使用道具 举报

0

主题

9

帖子

7.00

积分

新手上路

Rank: 1

积分
7.00
发表于 2021-3-8 22:15:01 | 显示全部楼层
问题又来了,既然在析构函数前加virtual不是为了让子类继承,
1>>>>那加它作什么?
2>>>>难道说加了virtual之后,子类的析构函数就加入到 虚函数表 中了?(此时基类,子类析构函数都调用)
而不加virtual,子类就不加入虚函数表,也就在最后不被调用?(此时只调用基类的析构函数)

---------------------------------------------ANS-----------------------------------------------------------------------------
>>>>加virtual的作用你自己已经明白了,是为了防止使用父子类指针转换时引起资源泄露.这个在Effective c++ 里面有说.
>>>>不是虚函数,就不会进入虚函数表.这时候虚函数表里没有指向析构的索引.不加virtual的时候子类析构也会自动调用父类的析构.这和前面说的扩展是相同的.此时函数的行为与不加virtual的时候不同.

关于虚函数表的具体内容可看<inside c++ object modal> chap 1.1 或者是看<thinking in c++>,虚析构函数的内容在<inside c++ object modal>里面也有讲解.

virtual的作用是用来实现"多态",这个才是它的本质.
回复

使用道具 举报

1

主题

13

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2021-3-8 22:30:02 | 显示全部楼层
看来我语言表达能力确实有待加强 -_-b

书上写的基类的构造函数和析构函数派生类不能继承!

我再问(哎~~ ):

为什么 若没有virtual,delete p;就少调用一个析构函数?

我是这样理解virtual的:
派生类能继承来自父类除构造函数和析构函数外所有的成员和函数,如:
class Base
{
public:
Base();
~Base(){cout<<"Destructor Base!";};

virtual void Out1(){cout<<"In the Base!(Out1)";};
void Out2(){cout<<"In the Base!(Out2)";};

private:
int x;
int y;
}

class Derived:public Base
{
public:
Derived(int z);
~Derived(){cout<<"Destructor Derived";};

virtual void Out1(){cout<<"In the Derived!(Out1)";};
void Out2(){cout<<"In the Derived!(Out2)";};
void Out3(){cout<<"In the Derived!(Out3)";};

private:
int z;
};

int  main()
{
Base bb,*p;
Derived dd(123);
*p=bb;
*p->Out1();     //In the Base!(Out1)
*p->Out2();     //In the Base!(Out2)
*p=dd;
*p->Out1();     //In the Derived!(Out1)
*p->Out2();     //In the Base!(Out2)

delete p;
return 0;
};

上面的Derived继承了Base的除
Base(); ~Base();
两个函数外所有的成员(x,y)和函数(Out1,Out2),当此时我定义一个基类的指针去访问派生类对像来实现多态,但由于是基类的指针,所以只能访问派生类继承自基类的函数(如Out1,此时在Derived中的Out2前没有加virtual虽然它的名子和Base的Out2相同,但在Derived中被Derived的Out2隐藏掉),不能访问Derived自己新定义的函数(如:Out2,Out3,当然也包括Dervied 的构造函数与析构函数).

好了,既然*p能访问派生类的Out1又可以访问基类的Out1,就可以为*p赋不同的对像(bb,dd)来调用不同对像的Out1,实现多态.
但对于Out2就不行了,因为虽然*p=bb 指向的是派生类的对像,*p->Out2();原意是想调用派生类的Out2但由于Out2是派生类中新加入的函数,用基类的指针不能访问,所以当我用*p->Out2();时编译器就会在派生类中找到那个被隐藏起来的从基类继承下来的同名函数Out2();

下面的理解感觉有问题!!!!

与上面Out2()的道理相似,当我调用 delete p;时由于p是基类类型的指针,虽然它现在指向派生类对象,但它在派生类中找不到从基类中继承下来的析构函数(构造函数与析构函数不能被继承)而~Derived()又是派生类新的函数,基类指针无权访问,就谈不上调用了,delete p;只能跳过这个析构函数,调用基类的析构函数(此时,由于z是派生类中的成员,调用基类的析构函数不能释放z的空间!造成了错误)
而当我在基类的析构函数前加上virtual后......派生类中又没有同名的析构函数,加它有什么用,但好像析构函数不要求同名..... 乱了乱了,后面的就不知道了

1>>>>我上面理解的对不对?
2>>>>为什么 若没有virtual,delete p;就少调用一个析构函数?
3>>>>为什么若有virtual,delete p;就调用两个析构函数?

谢谢各位了,写的比较多,因为很想知道里面究竟背着我发生了什么
回复

使用道具 举报

1

主题

13

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2021-3-8 22:45:01 | 显示全部楼层
4>>>(忘了说了)我在Essential C++中看到这样说:当类中有一个或多个virtual函数时,就应当把它的析构函数定义为virtual!  (??这跟基类有virtual函数有什么关系,不是说是因为怕派生类中的新定义成员空间不被释放而加的保险吗?)

打完了那些字才看到booook 的话,看来我是读的书太少了啊,也应了那个谁谁说的:现在不明白不要紧,往后见的多了,自然就明白了...
Effective C++ 正在邮递中,但现在我又很想知道,哎~ 看来我是个矛盾的人
回复

使用道具 举报

0

主题

1

帖子

2.00

积分

新手上路

Rank: 1

积分
2.00
发表于 2021-3-8 23:15:01 | 显示全部楼层
4:当类中有一个或多个virtual函数时,就应当把它的析构函数定义为virtual
-------
如果一个类中virtual函数的话,创建者就很明确的告诉你 这个类是用来继承的 很明显 析构当然要virtual了


lz也别灰心,,能有自己的看法才能看进去
回复

使用道具 举报

0

主题

5

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
发表于 2021-3-8 23:30:02 | 显示全部楼层
如果不是密封类就最好把析构函数都写成虚的,这是一个好习惯,可以避免很多问题
回复

使用道具 举报

0

主题

5

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
发表于 2021-3-8 23:45:02 | 显示全部楼层
当然对于执行效率要求很高的程序除外~~
带虚函数就会有虚表,就牵扯到动态联编,对效率会有一些影响,不过应用程序更多的还是结构比较重要。。。
回复

使用道具 举报

0

主题

9

帖子

7.00

积分

新手上路

Rank: 1

积分
7.00
发表于 2021-3-9 00:00:01 | 显示全部楼层
2>>>>为什么 若没有virtual,delete p;就少调用一个析构函数?
3>>>>为什么若有virtual,delete p;就调用两个析构函数?

如果基类的析构不是virtual, 那么Base *p; p = new Derive; delete p;这个时候就调用基类的析构。而如果基类是virtual,那么delete p 这个操作就执行多态,执行子类的析构。你只需要理解虚函数的行为就可以了。也许你写多了代码就自然明白了吧。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|CopyRight © 2008-2023|verysource.com ( 京ICP备17048824号-1 )

快速回复 返回顶部 返回列表