`
javasogo
  • 浏览: 1834828 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

什么时候你需要一个虚构函数是虚的

 
阅读更多

<!-- [if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!-- [if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!-- [if gte mso 10]> <mce:style><!-- /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} --> <!-- [endif]---->

什么时候你需要一个虚构函数是虚的?
//z 2011-06-10 22:58:36@is2120.CSDN
转载请注明出处
tag: virtual destructor dtor
虚构函数 虚虚构函数 虚函数
你的c++ 对象的虚构函数何时应为virtual

首先,拥有一个虚虚函数意味着什么?

嗯,拥有一个虚函数意味着什么?

如果一个 函数是虚函数,那么在一个object 上调用一个函数总是调用由the most heavily derived class 实现的method 。如果method 不是virtual ,那么实现就对应到对象指针编译时的类型。

举例如下:
For example, consider this:

class Sample {

public:

void f();

virtual void vf();

};

class Derived : public Sample {

public:

void f();

void vf();

}

void function()

{

Derived d;

Sample* p = &d;

p->f();

p->vf();

}

p->f() 调用将导致调用Sample::f ,因p 是一个指向Sample 的指针。真实object 是派生的类型,但是指针仅仅是一个指向Sample 的指针。这里使用了指针类型(相对实际的object type ),因f 不是virtual 的。
//z 2011-06-10 22:58:36@is2120.CSDN 转载请注明出处

另一方面,p->vf() 调用将导致一个Derived::vf 的调用,the most heavily derived type ,因vfvirtual 的。


是的,你已知道这些了。

Virtual destructors 完全同样的工作方式。只是你很少显式调用dtor 。更确切地说,当一个自动变量出其scope 时或者你删除(new,delete )一个对象时就会调用dtor

void function()

{

Sample* p = new Derived;

delete p;

}


既然Sample 没有一个virtual destructor ,那么delete p 将调用该指针类的dtor ,而不是most derived typdedtorDerived::~Derived() )。如你所见,在上述情景中这是不正确的事情。

有了这些信息在手,你就可以回答开头的问题了。

如果你一个类满足了下面两个条件,那么必须有一个virtual destructor

. 你会delete p
.p
实际会指向一个派生类

一些人认为你需要一个virtual dtor 当且仅当你有一个virtual method 。这在双向上都是不正确的。

下面就是没有virtual method 但是仍旧需要 virtual dtor 的例子:
class Sample { };

class Derived : public Sample

{

CComPtr<IStream> m_p;

public:

Derived() { CreateStreamOnHGlobal(NULL, TRUE, &m_p); }

};

Sample *p = new Derived;

delete p;

delete p 将导致调用 Sample::~Sample 而不是Derived::~Derived 。这将导致stream m_p 的一个泄漏。
The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.

然后下面就是一个例子,一个classvirtual method 但是不需要一个virtual dtor
class Sample { public: virtual void vf(); }

class Derived : public Sample { public: virtual void vf(); }

Derived *p = new Derived;

delete p;

既然指针类型和对象类型一致,那么相应正确的dtor 就会别调用。这个模式在com 对象中经常如此,这些对象常常根据对应的接口输出几个virtual methods ,但是对象本身由其自身的实现来销毁而不是从一个基类指针(注意没有com 接口包含一个虚拟dtor
知道何时让你的dtorvirtual 或者不是取决于人们如何使用你的类。如果c++ 有一个“sealed ”关键字,那么规则就会简单多了:如果在一个非sealed class 上调用“delete p ”,那么class 需要有一个virtual dtor

『虚幻的 sealed 关键字使得它是显式的,当一个类能够作为另一个类的基类』
//z 2011-06-10 22:58:36@is2120.CSDN 转载请注明出处



补充材料:

class A{
public:
A(){}
~A(){}
}

class B{
B(){}
~B(){}
}

A * a = new B;

delete a;

在标准中有有关规格说明如下( <!-- [if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!-- [if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!-- [if !mso]> <object classid="clsid:38481807-CA0E-42D2-BF39-B33AF135CC4D" id=ieooui> </object> <style> st1/:*{behavior:url(#ieooui) } </style> <![endif]--><!-- [if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} </style> <![endif]-->5.3.5 /3 ):

<!-- [if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!-- [if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!-- [if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} </style> <![endif]-->

if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

从而如上的情况是UB(未定义的)

分享到:
评论

相关推荐

    C_虚构造函数和虚析构函数.pdf

    C_虚构造函数和虚析构函数.pdf

    C++虚析构函数的使用分析

    首先解释一下虚构函数和指针之间是如何交互的,以及虚析构函数的具体含义。例如以下代码,其中SomeClass是含有非virtual析构函数的一个类:SomeClass *p= new SomeClass;. . . . . .delect p;为p调用delect时,会...

    c++多态性与虚函数习题答案 (2).docx

    例如,可以定义一个基类指针数组,数组中包含不同派生类的对象,然后通过基类指针调用虚函数,根据实际对象的类型自动执行对应的实现。 实现运行时多态并不一定需要使用虚函数,还需要通过基类指针或引用来调用函数...

    C++深刻理解继承与虚构

    - 如果一个类作为基类存在,并且可能有派生类对象通过基类指针删除,那么应该将析构函数声明为虚函数,以确保正确的清理顺序。例如: ```cpp class Base { public: virtual ~Base() {} // 虚析构函数 }; ``` ...

    C++派生类的构造函数

    7. 多重继承与构造函数:如果派生类从多个基类派生,那么在派生类的构造函数中需要为每一个基类调用相应的构造函数。 8. 虚构造函数:C++不支持虚构造函数,这意味着在派生类的指针或引用上调用构造函数将不会调用...

    习题8及其解答(第二版).doc

    当在派生类中重载一个虚函数时,必须保持函数名、参数列表(包括参数的个数、类型和顺序)以及返回类型与基类中虚函数完全一致,这是实现多态性的关键。 4. 构造函数与析构函数: 构造函数和析构函数不能被声明为...

    C++编程思想(中文)\21

    `Shape`的构造函数接受一个枚举值作为参数,根据该值决定创建哪种类型的对象,并将其赋值给`S`。 ##### 优势与局限 - **优势**: - 封装性好:所有派生类的定义可以隐藏在实现文件中,提高代码的可维护性和扩展性...

    C++复习题1.docx

    15. 虚构造函数:C++不支持虚构造函数,因为构造函数不参与多态调用。虚析构函数用于正确销毁派生类对象,即使通过基类指针。 16. Windows应用程序结构:MFC中的典型结构包括单文档界面(SDI)、多文档界面(MDI)...

    Bjarne Stroustrup的FAQ:C++的风格与技巧

    这种机制对于多态非常重要,因为虚函数表需要一个固定的偏移量来访问。 ##### (4) 我必须在类声明处赋予数据吗? 不,你可以选择在构造函数初始化列表中初始化数据成员。这样做可以在构造函数执行之前就完成数据...

    C/C++语言 面试考题

    1. **虚构造函数/析构函数**:虚构造函数允许子类对象通过基类指针或引用进行构造,而虚析构函数确保在删除对象时正确调用派生类的析构函数,防止资源泄露。 2. **纯虚函数**:纯虚函数(virtual void func()=0)...

    C++设计模式代码资源10_prototype.zip

    2. **使用虚构造函数**:虽然C++不直接支持虚构造函数,但可以使用工厂方法或者`clone()`函数模拟这一行为。 3. **模板方法**:如果多个类具有相同的克隆逻辑,可以使用模板类来实现,这样可以减少代码重复。 ...

    C++编码规范.

    **原则3.2 函数原型声明放在一个头文件中** - 将函数原型声明放在头文件中,方便引用。 - 示例:`#include "functions.h"`。 **原则3.3 函数无参数一定要用void标注** - 无参数的函数应在括号内使用 `void`。 - ...

    c++编码规范

    - **虚构造函数**:在需要进行多态实例化的情况下,基类应声明虚构造函数。 #### 第4章 类的设计与声明 - **描述一组对象**:类应描述一组共享相同属性和行为的对象。 - **私有成员**:类的成员变量通常是私有的,...

    戏说面向对象 c#版

    C#支持单一继承,即一个子类只能有一个父类,但可以通过接口实现多重继承的效果。通过使用`:`运算符,我们可以声明一个类是另一个类的子类。 多态是指同一种行为在不同的对象上表现出不同的形式。在C#中,多态主要...

    虚拟现实技术.docx

    虚拟现实技术的核心在于创造一个令人信服的虚拟环境,使用户感觉自己置身于该环境中。 #### 二、虚函数与实函数 在数学领域,尤其是在处理虚拟现实技术中的数学模型时,“虚函数”和“实函数”的概念被用来描述...

    设计模式精解-GoF 23种设计模式解析附C++实现源码

    1. **单例模式(Singleton)** - 确保一个类只有一个实例,并提供一个全局访问点。在C++中,可以使用静态成员变量或私有构造函数与友元函数来实现。 2. **工厂方法模式(Factory Method)** - 定义一个用于创建对象...

    inside the c++ object model

    10. **MFC(Microsoft Foundation Classes)**:作为一个标签,MFC是微软提供的C++库,用于开发Windows应用程序。理解C++对象模型也有助于更好地使用和定制MFC框架。 《深入浅出C++对象模型》.chm文件可能是该书的...

    C#试题(基础)

    ### C#基础知识知识点详解 #### 一、单项选择题知识点解析 **1....- **知识点:** C#程序从编写到运行的完整流程包括输入(编写...- **知识点:** 函数重载是指在同一个作用域内定义多个同名但参数列表不同的函数。正确。

    易语言仿口袋妖怪小游戏源码

    对于想要学习游戏开发或者对易语言感兴趣的开发者来说,这是一个很好的学习资源。 《口袋妖怪》是一款广受欢迎的角色扮演游戏,玩家在游戏中扮演训练师,捕捉、训练各种虚构的生物——口袋妖怪,并与它们一起战斗。...

Global site tag (gtag.js) - Google Analytics