virtual

[C++] Virtual Destructor

做~自己de王妃 提交于 2019-12-07 08:20:17
假定有如下的两个类: class Base { // some virtual methods }; class Derived : public Base { ~Derived() { // Do some important cleanup } }; 然后有下面的语句: Base *b = new Derived(); delete b; // Here's the problem! 行为未定义!!! 解决办法是将 Base 的析构函数定义为 virtual 。 当删除指针时,如果指针的对象的静态类型不同于它的动态类型,那么:静态类型必须为基类,且基类必须定义 virtual destructor,否则行为就是未定义的。 而且,如果没有将基类的析构函数定义为 virtual, 当delete指针时,只有基类的析构函数被调用,派生类的析构函数不会被调用,这会导致资源泄漏,总而言之,如果将基类用于多态,那么,基类的析构函数应该总是定义 virtual destructor。 [Effective C++] 假定有如下表示敌方目标的类,静态变量用于记录对象数目: class EnemyTarget { public: EnemyTarget() { ++numTargets; } EnemyTarget(const EnemyTarget&) { ++numTargets; }

Effective C++学习笔记一

ε祈祈猫儿з 提交于 2019-12-07 08:18:00
条款 01 : 视C++ 为一个语言联邦 Ø C Ø Object-Oriented C++ Ø Template C++//泛型编程部分( generic programming ) Ø STL C++ 高效编程守则视情况而变化,取决与你使用 C++ 的 哪一部分。 条款 02 : 尽量以 const, enum , inline替换#define Ø 对于单纯常量,最好以 const 对象或者 enums 替换#defines 。 Ø 对于形似函数的宏,最好改用 inline 函数替换 #defines 。 条款 03 : 尽可能使用 const Ø 声明为 const 可帮助编译器侦测出错误用法。 Ø 编译器强制实施 bitwise constness, 但你编写程序时应该使用 “ 概念 上的常量性 ” 。 Ø 当const 和 non-const 成员函数有 着实质等价的实现时,令 non-const 版本调用 const 版本 可避免代码重复。 条款 04 : 确定对象被使用前已先被初始化 Ø C++ 成员初始化次序是固定的 ,class 的成员变量总是以其声明次序被初始化。但是 它们可以在成员初值列中以不同的次序出现,没有影响。 Ø 构造函数中注意分清初始化操作和赋值操作。初始化是在成员初始化列表里面,而赋值是在构造函数本体里面进行的。 Ø 构造函数最好使用成员初值列

Effective C++学习笔记

非 Y 不嫁゛ 提交于 2019-12-07 08:14:16
条款1:视C++为一个语言联邦 C++有4大组件:C、Object-OrientedC++、Template C++、STL。 条款2:尽量以const、enum、inline替换#define 对于单纯常量,最好以const对象或enums替换#define。 对于形似函数的宏,最好改用inline函数替换#defines。 条款3:尽可能用const 将某些东西声明为const可帮助编译器侦测出错误用法,const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。 当const和non-const成员函数有着实质等价的代码时,令non-const版本调用const版本可避免代码重复。 条款4:确定对象被使用前已被初始化 为内置对象进行手工初始化,因为C++不保证初始化它们。 构造函数最好使用成员初始化列表(memberinitialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,其排列次序应该和它们在类中的声明次序相同。 为避免“跨编译单元之初始化次序”问题,请以localstatic对象替代non-local static对象。 条款5:了解C++默默编写并调用了那些函数 编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。

条款:07 为多态基类声明virtual析构函数

混江龙づ霸主 提交于 2019-12-07 08:03:02
Effective C++中说过: 1、带有多态性质的base class应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数; 2、Classes的设计目的如果不是作为base class使用, 或者不是为了具有多态性质,就不该声明virtual 析构函数。 #ifndef __EFFECTIVE_H__ #define __EFFECTIVE_H__ /*include*/ #include <iostream> using namespace :: std; class baseClass { public : baseClass() { cout << "base Class construction function" << endl; } ~baseClass() { cout << "base Class destruction function" << endl; } }; class derivedClass : public baseClass { public : derivedClass() { cout << "derived Class construction function" << endl; } ~derivedClass() { cout << "derived Class

条款07:为多态基类声明virtual析构函数

孤街醉人 提交于 2019-12-07 08:01:53
c++面向对象特性其中有多态这个特性,平时使用时用基类指针指向子类对象。当我们进行内存回收的时候调用 delete (基类指针),此实如果基类有一个non-virtual析构函数。这种情况下会导致派生类的对象没有被销毁,只有基类部分会被销毁掉,于是造成一个诡异的“局部销毁”对象。这样的后果会形成资源泄露、数据结构被破坏、而且很难找出原因。 消除这个问题的做法很简单:给base class一个virtual析构函数。此后删除derived class对象就会如你想要的那般,将整个对象都销毁,包括derived class成分。 如果class不含virtual函数,通常表示它并不意图被用做一个base class。当class不企图被当作base class,令其析构函数为virtual往往是个馊主意。因为使用virtual函数会导致对象中有一个虚表指针(vptr),该表存在的目的就是为了实现多态的,但是会占用内存空间。这样做的结果会导致该对象不再和其他语言(如C)内的相同声明有着一样的结构(因为其它语言对应物并没有vptr),因此也不再具有移植性。 请记住 polymorphic(带多态性质的)base class应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。 Classes的设计目的如果不是作为base

条款31:将文件间的编译依存关系降至最低

蹲街弑〆低调 提交于 2019-12-07 08:01:21
条款31:将文件间的编译依存关系降至最低 (Minimize compilation dependencies between files.) 内容: 在你们的开发团队中,一些有经验的工程师时不时地会教导新手一些基本的编程原则,其中"将接口从实现中 分离"可能是他(她)要你必须牢记原则,因为C++并没有把它做的很好,这只能靠我们在平时的编写代码中注意这 一点了,如果你不小心违背了这一原则,可能招致的后果就是:当你轻微的修改了某个类的实现,注意不是接口的 时候,再次重新BUILD一次你的工程,Oh,My God!很多文件被重新编译和链接了,Build的时间大大超出你的预期, 而这种事情的发生,估计你当时就会只恨编译器的BUILD的速度太慢,效率太低.呵呵.避免陷入这种窘境的一种有 效的方法就是本条款要提出的内容:将文件间的编译依存关系降至最低. 现在假设你写了一个Person类,一般你会这么构思你的代码: #include <string> #include "date.h" #include "address.h" class Person{ public: Person(const std::string& name,const Date& birthday,const Address& addr); std::string name()const; std::string

《Effective C++》item7:为多态基类声明virtual析构函数

╄→尐↘猪︶ㄣ 提交于 2019-12-07 07:34:32
今天研究了一下虚函数,解决了一直困扰的几个问题,尤其是在前段时间找工作面试的时候说不清的几个问题,早知如此,何必当初哎! Questions: (1)为什么要用虚函数? (2)为什么要定义virtual析构函数? (3)什么时候该定义virtual析构函数和什么时候不该定义virtual析构函数? Answers: (1)为什么要用虚函数? C++中的虚函数的作用主要是实现了 多态的机制 。关于多态,简而言之就是 用父类类型的指针指向其子类的实例 ,然后通过父类的指针调用实际子类的成员函数 。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。Java中的多态是通过interface和abstract来实现的,java没有virtual一说! 定义一个函数为虚函数,不代表函数为不被实现的函数。 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 而在Java中,只要子类实现了interface中定义的函数,那么接口声明的引用一定会调用子类的这个函数,如果子类没有定义,则调用接口中的函数! 定义一个函数为纯虚函数,才代表函数没有被实现。定义他是为了实现一个接口,起到一个规范的作用,规范继承这个。类的程序员必须实现这个函数。 先看一个 不使用virtual 的例子: 该程序先定义了一个基类:TimeKeeper,基类中有一个实现了的函数print

C++继承关于析构函数的问题

不打扰是莪最后的温柔 提交于 2019-12-07 07:33:37
析构函数的问题 关于C++中析构函数的作用,不再详述。 在c++继承中,也知道要把父类的析构函数用 virtual 修饰 关于析构函数的基本使用方法,也是很简单。 但是!最近在工作中,遇到了一个关于析构函数很奇怪的问题!导致查了半天的Bug,才发现是少声明了一个析构函数。 首先,存在Bug的代码,大体如下: // Base.h # define USE_EXPROT_ __attribut((visibility("default")) class USE_EXPROT_ Base { public : Base ( int id, int * p) : m_id (id) , m_p (p) {} public : int m_id; int * m_p; } // Child.h #include "Base.h" class Child : public Base { public : Child ( int id, int * p) : Base (id, p) {} virtual ~Child() {} } 上述代码编译中,没有报错。但是在运行中,有一处关于“Base*”的指针,虽然做了非NULL判断,但使用时总会出错(野指针)。 结果查了半天的Bug,最终如下修改解决了。 // Base.h # define USE_EXPROT_ __attribut(

析构函数与virtual

别来无恙 提交于 2019-12-07 07:32:24
作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的。因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性。这个类可能会被继承,并且会通过基类的指针指向子类对象”,因此基类的析构函数是否为虚将决定子类的对象是否被析构。 #include <iostream.h> struct A { virtual ~A() {cout<<"~A()\n";} }; struct B: public A { ~B() {cout<<"~B()\n";} }; void main() { A* p = new B; delete p; } 如果 A 的析构函数不是virtual的,那么此时就不是先调用B的析构函数再调用A的析构函数。 输出为: ~A(); 如果 A 的析构函数为virtual,则先~B(),再~A(),输出为: ~B(); ~A(); 类如果会被派生的话,析构函数一般都应该定义为virtual的,主要不是防止内存泄露,而是为了正确的析构。如果是个封闭类(即不再被派生),就不要定义为virtual的。虚函数毕竟耗费较大的。 不用virtual 的几种情况: 作为非公有基类。仅作为 private base class 使用的 class 不需要使用虚拟析构函数。 不作为接口使用的基类。

为什么析构函数要声明成virtual

不羁的心 提交于 2019-12-07 07:30:06
(zz)为什么析构函数要声明成virtual 2011-03-08 15:43:00 | 分类: reproduct | 字号 订阅 为什么析构函数要声明成virtual呢? 因为,如果 delete 一个基类的指针时, 如果它指向的是一个子类的对象,那么析构函数不为虚就会导致无法调用子类析构函数,从而导致资源泄露。 当然,另一种做法是将基类析构函数设为 protected. 如果一个类要被使用成多态(polymorphic)的,那么这个virtual是必须的。比如: #include <iostream> class Animal { char* ap; public: Animal() { ap = new char; std::cout << "Animal ctor" << std::endl; } virtual void foo() { std::cout << "Animal::foo" << std::endl; } virtual ~Animal() { std::cout << "Animal dtor" << std::endl; delete ap; } }; class Dog : public Animal { char* dp; public: Dog() { dp = new char; std::cout << "Dog ctor" << std: