C++虚函数注意事项

目录
  • 一、虚函数注意事项
    • 1.构造函数
    • 2.析构函数
    • 3.友元
    • 4.没有重新定义
    • 5.重新定义将隐藏方法

文章转自公众号:Coder梁(ID:Coder_LT)

一、虚函数注意事项

在之前的文章当中,我们已经讨论了虚函数的使用方法,也对它的原理进行了简单的介绍。

这里简单做一个总结:

  • 在基类的方法声明中使用关键字virtual可以声明虚函数
  • 加上了virtual关键字的函数在基类以及派生类和派生类再派生出来的类中都是虚的
  • 在调用虚函数时,程序将会根据对象的类型执行对应的方法而非引用或指针的类型
  • 在定义基类时,需要将要在派生类中重新定义的类方法声明为虚,如析构函数

除了这些之外,我们还有一些其他需要注意的事项。

1.构造函数

构造函数不能是虚函数,创建派生类对象时将调用派生类的构造函数,而非基类的构造函数,毕竟构造函数是根据类名调用的。

一般我们会在派生类中调用基类的构造函数,这其实不是继承机制,所以将类构造函数声明为虚没有意义。

2.析构函数

前文说过析构函数应该是虚函数,除非类不被继承。

因为派生类当中往往含有独有的成员变量,如果析构函数非虚,那么会导致在对象析构时仅调用基类的析构函数,从而导致独有的成员变量内存不被释放,引起内存泄漏。

所以通常我们会将析构函数设置成virtual,即使不用做基类也不会引起错误,至多只会影响一点效率。但在大型合作开发的项目当中,许多组件和类都是共享的,我们往往无法保证我们开发的类是否会被其他开发者继承,因此设置虚析构函数也是一种常规做法。

3.友元

友元函数不能是虚函数,因为友元不是类成员,只有成员函数才能是虚函数。

如果我们希望友元函数也能实现类似虚函数的功能, 我们可以在友元函数当中使用虚函数来解决。

4.没有重新定义

如果派生类当中没有重新定义虚函数,那么将使用该函数的基类版本。如果派生类位于派生链中,如B继承了A,C继承了B这种情况,那么派生类将会使用最新的虚函数版本。

5.重新定义将隐藏方法

我们来看一个例子:

class Mammal {
 private:
  string name;
 public:
  Mammal(string n): name(n) {}
  virtual void speak() const {
   cout << "can't say anything" << endl;
  }
};

class Human : public Mammal{
 private:
  string job;
 public:
  Human(string n, string j): Mammal(n), job(j) {}
  virtual void speak(const string st) const {
   cout << "i'm human" << endl;
  }
};

我们在父类当中定义了一个无参虚函数speak,而在子类Human当中也定义了一个需要传入一个string类型的虚函数speak

我试了一下,在我的g++编译器当中,会报错:

但根据C++ Primer中的说法,在一些古老的编译器当中,可能不会报错,甚至可能连警告都没有。

在这类编译器当中,我们重新定义父类中的虚函数,这样的重新定义不会生成两个重载版本,而是隐藏了父类无参的版本,只保留了接受string类型的版本,这种情况有别于函数重载。

在派生类当中重新定义函数,不是使用相同的函数特征标覆盖基类声明,而是隐藏同名的基类方法,不管函数特征标如何。

C++ Primer当中给出了两条经验规则:

如果重新定义继承的方法,应该保证与原来的原型完全相同,唯一的例外是返回的类型,如果基类返回的是基类的引用或指针,派生类可以改成派生类的引用或指针:

class Mammal {
 private:
  string name;
 public:
  Mammal(string n): name(n) {}
  virtual Mammal* build();
};

class Human : public Mammal{
 private:
  string job;
 public:
  Human(string n, string j): Mammal(n), job(j) {}
  virtual Human* build();
};

如果基类声明被重载了,那么应该在派生类中声明所有的基类版本:

class Mammal {
 private:
  string name;
 public:
  Mammal(string n): name(n) {}
  virtual void speak() const ;
     virtual void speak(int n) const;
     virtual void speak(const string st) const;
};

class Human : public Mammal{
 private:
  string job;
 public:
  Human(string n, string j): Mammal(n), job(j) {}
  virtual void speak() const ;
     virtual void speak(int n) const;
     virtual void speak(const string st) const;
};

如果我们只重新定义了一个版本,那么另外两个版本将隐藏。

但这可能和编译器版本有关,在新版的编译器当中似乎取消了这一设定。

我尝试了一下,发现并不会隐藏,一样可以顺利调用父类方法。

class Mammal {
 private:
  string name;
 public:
  Mammal(string n): name(n) {}
  virtual void speak() const {
   cout << "can't say anything from empty" << endl;
  }
  virtual void speak(const string st) const {
   cout << "can't say anything from string input" << endl;
  }
};

class Human : public Mammal{
 private:
  string job;
 public:
  Human(string n, string j): Mammal(n), job(j) {}
  virtual void speak(const string st) const {
   cout << "i'm human" << endl;
  }
};

int main() {
 Mammal *m = new Human("man", "spiderman");
 m->speak();
 return 0;
}

到此这篇关于EC++虚函数注意事项的文章就介绍到这了,更多相关EC++虚函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++虚函数表深入研究

    目录 探索虚函数表结构 继承基类重写虚函数 多基类继承 虚函数表 寻找被覆盖的虚函数 总结 面向对象的编程语言有3大特性:封装.继承和多态.C++是面向对象的语言(与C语言主要区别),所以C++也拥有多态的特性. C++中多态分为两种:静态多态和动态多态. 静态多态为编译器在编译期间就可以根据函数名和参数等信息确定调用某个函数.静态多态主要体现为函数重载和运算符重载. 函数重载即类中定义多个同名成员函数,函数参数类型.参数个数和返回值不完全相同,编译器编译后这些同名函数的函数名会不一样,也就是说

  • 关于C++虚函数与静态、动态绑定的问题

    覆盖:如果派生类中的方法,和基类继承来的某个方法,返回值.函数名.参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数,它们之间成为覆盖关系:也就是说派生类会在自己虚函数表中将从基类继承来的虚函数进行替换,替换成派生类自己的. 静态绑定:编译时期的多态,通过函数的重载以及模板来实现,也就是说调用函数的地址在编译时期我们就可以确定,在汇编代码层次,呈现的就是 call 函数名: 动态绑定:运行时期的多态,通过派生类重写基类的虚函数来实现.在汇编代码层次,呈现

  • C++虚函数表和虚析构介绍

    目录 1.虚函数表 2.虚析构 1.虚函数表 虚函数表是C++实现多态的基础,多态是面向对象的三大特性之一,多态有利于提高代码的可读性,便于后期代码的扩展和维护.我们都知道多态的实现是基于虚函数表,那么虚函数表是什么时候创建的呢?虚函数表是怎么实现多态的功能的呢? 首先应该明确多态也称为动态多态,他是在程序运行时候确定函数地址的,也就是程序在运行时,如果类成员函数加了virtual关键字,就会建立一个虚函数指针(vfptr)指针指向一个虚函数表,这个虚函数表就保存了虚函数的地址,子类继承父类也自

  • 详解如何实现C++虚函数调用汇编代码

    虚函数(代码段地址)被存放在虚函数表中,调用虚函数的流程是这样子的:先获取虚函数表的首地址,然后根据目标虚函数在虚函数表的位置(offset偏移)取出虚函数表中的虚函数地址,最后去call这个虚函数(地址),就完成虚函数的调用.这个虚函数调用的流程在汇编代码中可以最直观的反映出来. 在排查软件异常或崩溃时,我们时常要借助汇编代码的上下文去辅助分析问题.读懂C++虚函数调用的汇编代码实现,对于搞懂汇编代码的上下文时很有好处的.今天我们就来看看虚函数调用的汇编代码实现. 比如如下的C++代码: //

  • 聊一聊C++虚函数表的问题

    之前只是看过C++虚函数表相关介绍,今天有空就来写代码研究一下. 面向对象的编程语言有3大特性:封装.继承和多态.C++是面向对象的语言(与C语言主要区别),所以C++也拥有多态的特性. C++中多态分为两种:静态多态和动态多态. 静态多态为编译器在编译期间就可以根据函数名和参数等信息确定调用某个函数.静态多态主要体现为函数重载和运算符重载. 函数重载即类中定义多个同名成员函数,函数参数类型.参数个数和返回值不完全相同,编译器编译后这些同名函数的函数名会不一样,也就是说编译期间就确定了调用某个函

  • c++虚函数与虚函数表原理

    目录 1.什么是虚函数? 2.虚函数会影响类的内存 3.了解虚函数表--->通过虚函数表的指针去访问数据 4.虚函数声明 1.什么是虚函数? 用virtual 修饰的成员函数叫虚函数 小知识: 没有虚构造函数        不写虚函数,没有默认的虚函数 普通函数不影响类的内存: class MM { public: void print() { cout << "普通函数"<< endl; //普通函数不影响类的内存<--->普通函数存在另一段

  • C++虚函数注意事项

    目录 一.虚函数注意事项 1.构造函数 2.析构函数 3.友元 4.没有重新定义 5.重新定义将隐藏方法 文章转自公众号:Coder梁(ID:Coder_LT) 一.虚函数注意事项 在之前的文章当中,我们已经讨论了虚函数的使用方法,也对它的原理进行了简单的介绍. 这里简单做一个总结: 在基类的方法声明中使用关键字virtual可以声明虚函数 加上了virtual关键字的函数在基类以及派生类和派生类再派生出来的类中都是虚的 在调用虚函数时,程序将会根据对象的类型执行对应的方法而非引用或指针的类型

  • C++ 虚函数与纯虚函数的使用与区别

    目录 什么是虚函数: 虚函数的注意事项: 纯虚函数 纯虚函数的注意事项: 虚函数与纯虚函数区别 什么是虚函数: 虚函数 是在基类中使用关键字 virtual 声明的函数,在C++ 语言中虚函数可以继承,当一个成员函数被声明为虚函数之后,其派生类中的同名函数都自动生成为虚函数, 虚函数主要体验C++的多态方面,(多态是参数个数和类型相同而实现功能不同的函数) 为了更好的里面虚函数请看下面的demo #include <iostream> #include <string> using

  • C++ 虚函数与纯虚函数代码详解

    目录 什么是虚函数: 虚函数的注意事项: 存虚函数 总结 什么是虚函数: 虚函数 是在基类中使用关键字 virtual 声明的函数,在C++ 语言中虚函数可以继承,当一个成员函数被声明为虚函数之后,其派生类中的同名函数都自动生成为虚函数, 虚函数主要体验C++的多态方面,(多态是参数个数和类型相同而实现功能不同的函数) 为了更好的里面虚函数请看下面的demo #include <iostream> #include <string> using namespace std; cla

  • C++ 中const修饰虚函数实例详解

    C++ 中const修饰虚函数实例详解 [1]程序1 #include <iostream> using namespace std; class Base { public: virtual void print() const = 0; }; class Test : public Base { public: void print(); }; void Test::print() { cout << "Test::print()" << end

  • 探讨C++中不能声明为虚函数的有哪些函数

    常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数. 多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层 函数不属于成员函数,是不能被继承的 2.为什么C++不支持构造函数为虚函数? 这个原因很简单,主要是从语义上考虑,所以不支持.因为构造函数本来就是为了

  • 如何获取C++类成员虚函数地址的示例代码

    本文主要给大家介绍了关于如何获取C++类成员虚函数地址的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍: 1.GCC平台 GCC平台获取C++成员虚函数地址可使用如下方法[1]: class Base{ int i; public: virtual void f1(){ cout<<"Base's f1()"<<endl; } }; Base b; void (Base::*mfp)() = &Base::f1; printf(&qu

  • PHP5中虚函数的实现方法分享

    请看下面的代码: 复制代码 代码如下: <?php class A { public function x() { echo "A::x() was called.\n"; } public function y() { self::x(); echo "A::y() was called.\n"; } public function z() { $this->x(); echo "A::z() was called.\n"; } }

  • C/C++杂记 虚函数的实现的基本原理(图文)

    1. 概述 简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针.例: 其中: B的虚函数表中存放着B::foo和B::bar两个函数指针. D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz. 提示:为了描述方便,本文在探讨对象内存布局时,将忽略内存对齐对布局的影响. 2. 虚函数表构造过程 从编译器的角度来说,

  • C++ 类中有虚函数(虚函数表)时 内存分布详解

    虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承.覆盖的问题,保证其容真实反应实际的函数.这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数. 这里我们着重看一下这张虚函数表.C++的编译器应该是

  • 简单解读C++中的虚函数

    虚函数 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 class A{ public: void print(){ cout<<"This is A"<<endl;} }; class B:public A{ public: void print()

随机推荐