c++ virtual函数 继承实现例子

合集下载

虚函数原理

虚函数原理

虚函数原理虚函数是 C++ 中一个非常重要的特性,它为面向对象编程提供了很强的支持。

虚函数的实现原理是通过虚函数表实现的,本文将介绍虚函数的概念、使用方法以及实现原理。

一、虚函数概念虚函数是指在基类中使用 virtual 关键字声明的成员函数,它的作用是允许在子类中对该函数进行覆盖。

具体来说,虚函数允许在子类中定义一个与基类中同名的函数,当使用子类对象调用该函数时,程序会动态的选择调用子类中的函数。

虚函数的语法如下:```class Base {public:virtual void foo();};```虚函数可以被重写(覆盖),也可以被继承,但是不能被 static 和 friend 修饰。

二、虚函数的使用使用虚函数需要满足一下条件:1.虚函数必须在公有的类成员函数列表中声明,并在类声明的内部定义。

2.虚函数必须在基类和派生类中以相同的参数列表进行定义。

下面是一个使用虚函数的简单例子:class Square: public Shape {public:Square(double s) : side(s) {}double getArea() { return side * side; }Shape 是一个基类,Square 是它的一个派生类,Square 中重写了 getArea() 函数,计算正方形的面积。

虚函数的实现原理是通过虚函数表实现的。

虚函数表是一个指针数组,存储了每个类中的虚函数指针。

当对象被创建时,会在其内存空间中创建一个指向虚函数表的指针,这个指针通常称为虚函数表指针(vptr),虚函数的调用就是通过这个指针完成的。

每个含有虚函数的类都有一个独立的虚函数表,虚函数表智能在类的第一个对象中存储,它包含了该类中所有虚函数的地址。

在派生类中,虚函数表通常继承自它的直接基类,并在此基础上添加或修改虚函数的地址。

这样如果在派生类对象中调用虚函数时,程序会先获得对象的虚函数表指针,然后通过该指针找到对应的虚函数地址来执行函数。

C语言中的面向对象

C语言中的面向对象

C语言中的面向对象(1)-类模拟和多态,继承在面向对象的语言里面,出现了类的概念。

这是编程思想的一种进化。

所谓类:是对特定数据的特定操作的集合体。

所以说类包含了两个范畴:数据和操作。

而C语言中的struct仅仅是数据的集合。

(liyuming1978@)1.实例:下面先从一个小例子看起输出结果:11It is B.c=13It is A.a=1It is B_Fun2.类模拟解说:我在网上看见过一篇文章讲述了类似的思想(据说C++编程思想上有更加详细的解说,可惜我没空看这个了,如果有知道的人说一说吧)。

但是就象C++之父说的:“C++和C 是两种语言”。

所以不要被他们在语法上的类似就混淆使用,那样有可能会导致一些不可预料的事情发生。

其实我很同意这样的观点,本文的目的也不是想用C模拟C++,用一个语言去模拟另外一个语言是完全没有意义的。

我的目的是想解决C语言中,整体框架结构过于分散、以及数据和函数脱节的问题。

C语言的一大问题是结构松散,虽然现在好的大型程序都基本上按照一个功能一个文件的设计方式,但是无法做到更小的颗粒化――原因就在于它的数据和函数的脱节。

类和普通的函数集合的最大区别就在于这里。

类可以实例化,这样相同的函数就可以对应不同的实例化类的变量。

自然语言的一个特点是概括:比如说表。

可以说手表,钟表,秒表等等,这样的描述用面向对象的语言可以说是抽象(继承和多态)。

但是我们更要注意到,即使对应于手表这个种类,还是有表链的长度,表盘的颜色等等细节属性,这样细微的属性如果还用抽象,就无法避免类膨胀的问题。

所以说类用成员变量来描述这样的属性。

这样实例并初始化不同的类,就描述了不同属性的对象。

但是在C语言中,这样做是不可能的(至少语言本身不提供这样的功能)。

C语言中,如果各个函数要共享一个变量,必须使用全局变量(一个文件内)。

但是全局变量不能再次实例化了。

所以通常的办法是定义一个数组。

以往C语言在处理这样的问题的时候通常的办法就是这样,比如说socket的号,handel等等其实都是数组的下标。

c++_Virtual用法

c++_Virtual用法

virtual用法一#includeusing namespace std;class A{public:virtual void display(){ cout<<"A"<<ENDL; }};class B : public A{public:void display(){ cout<<"B"<<ENDL; }};void doDisplay(A *p){p->display();delete p;}int main(int argc,char* argv[]){doDisplay(new B());return 0;}这段代码打印出的结果为B,但是当把A类中的virtual去掉之后打印出的就为A。

当基类中没有virtual的时候,编译器在编译的时候把p看做A类的对象,调用的自然就是A类的方法。

但是加上virtual之后,将dispaly方法变成了虚方法,这样调用的时候编译器会看调用的究竟是谁的实例化对象,这样就实现了多态的效果。

也就是说,当基类的派生类中有重写过基类的虚方法的时候,使用基类的指针指向派生类的对象,调用这个方法实际上调用的会是派生类最后实现的方法virtual用法二#includeusing namespace std;class Person{public: Person(){ cout<<"Person构造"<<ENDL; }~Person(){ cout<<"Person析构"<<ENDL; }};class Teacher : virtual public Person{public: Teacher(){ cout<<"Teacher构造"<<ENDL; }~Teacher(){ out<<"Teacher析构"<<ENDL; }};class Student : virtual public Person{public: Student(){ cout<<"Student构造"<<ENDL; }~Student(){ cout<<"Student析构"<<ENDL; }};class TS : public Teacher, public Student{public: TS(){ cout<<"TS构造"<<ENDL; }~TS(){ cout<<"TS析构"<<ENDL; }};int main(int argc,char* argv[]){TS ts;return 0;}这段代码的终端输出结果为:Person构造Teacher构造Student构造TS构造TS析构Student析构Teacher析构Person析构当Teacher类和Student类没有虚继承Person类的时候,也就是把virtual去掉时候终端输出的结果为:Person构造Teacher构造Person构造Student构造TS构造TS析构Student析构Person析构Teacher析构Person析构大家可以很清楚的看到这个结果明显不是我们所期望的。

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

C++中虚函数工作原理和(虚)继承类的内存占用大小计算一、虚函数的工作原理虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。

典型情况下,这一信息具有一种被称为vptr(virtual table pointer,虚函数表指针)的指针的形式。

vptr 指向一个被称为vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到vtbl。

当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的vptr 指向的vtbl,然后在vtbl 中寻找合适的函数指针。

虚拟函数的地址翻译取决于对象的内存地址,而不取决于数据类型(编译器对函数调用的合法性检查取决于数据类型)。

如果类定义了虚函数,该类及其派生类就要生成一张虚拟函数表,即vtable。

而在类的对象地址空间中存储一个该虚表的入口,占4个字节,这个入口地址是在构造对象时由编译器写入的。

所以,由于对象的内存空间包含了虚表入口,编译器能够由这个入口找到恰当的虚函数,这个函数的地址不再由数据类型决定了。

故对于一个父类的对象指针,调用虚拟函数,如果给他赋父类对象的指针,那么他就调用父类中的函数,如果给他赋子类对象的指针,他就调用子类中的函数(取决于对象的内存地址)。

虚函数需要注意的大概就是这些个地方了,之前在More effective C++上好像也有见过,不过这次在Visual C++权威剖析这本书中有了更直白的认识,这本书名字很牛逼,看看内容也就那么回事,感觉名不副实,不过说起来也是有其独到之处的,否则也没必要出这种书了。

每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就会为这个类创建一个虚函数表(VTABLE)保存该类所有虚函数的地址,其实这个VTABLE的作用就是保存自己类中所有虚函数的地址,可以把VTABLE形象地看成一个函数指针数组,这个数组的每个元素存放的就是虚函数的地址。

c++ 子类虚函数

c++ 子类虚函数

c++ 子类虚函数
在C++中,子类可以重写父类的虚函数。

首先,父类中的虚函
数必须使用关键字virtual进行声明,这样子类才能重写该函数。

当子类继承自父类并且重写了父类的虚函数时,子类的实例化对象
将调用子类中的虚函数而不是父类中的版本。

当定义子类的虚函数时,需要使用override关键字来明确指出
这是对父类虚函数的重写,这有助于提高代码的可读性和可维护性。

在子类中使用override关键字可以确保该函数确实是重写了父类中
的虚函数,如果不是,则会产生编译错误。

另外,子类还可以选择是否调用父类的虚函数。

在子类中重写
父类的虚函数时,可以使用作用域解析运算符::来显式调用父类的
虚函数,这样可以在子类中添加一些额外的逻辑而不影响父类的行为。

需要注意的是,在C++中,虚函数是通过指针来实现多态性的,因此在子类中重写父类的虚函数时,需要确保函数签名(参数列表
和返回类型)与父类中的虚函数相匹配,否则编译器将无法正确识
别这是重写,从而导致意外的行为。

总之,在C++中,子类可以通过重写父类的虚函数来改变或扩
展父类的行为,这为实现多态性和面向对象编程提供了便利。

同时,合理使用虚函数的重写可以提高代码的灵活性和可维护性。

C#虚函数virtual详解

C#虚函数virtual详解
为子类需要去重新实现的操作(override),我们可以称之做“热点”。而虚拟函数也是 OOP
中实现多态的关键之一。
还是上面的例子(C#):
class 飞禽 { public string wing; // 翅膀 public string feather; // 羽毛 …… // 其它属性和行为 public virtual bool Fly() // 利用关键字 virtual 来定义为虚拟函数,这是一个热点 { // 空下来让子类去实现 } } class 麻雀 : 飞禽 // 麻雀从飞禽继承而来 { …… // 定义麻雀自己特有的属性和行为 public override bool Fly() // 利用关键字 override 重载飞翔动作,实现自己的飞翔 { …… // 实现麻雀飞的动作 } } class 鹤 : 飞禽 // 鹤从飞禽继承而来 { …… // 定义鹤自己的特有的属性和行为 public override bool Fly() // 利用关键字 override 重载实现鹤的飞翔 {
d = new D(); // 实例化 d 对象,D 是 d 的实例类 a.Func(); // 执行 a.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 A,就为本身 4.执行实例类 A 中的方法 5.输出结果 Func In A b.Func(); // 执行 b.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 B,有重载的 4.执行实例类 B 中的方法 5.输出结果 Func In B c.Func(); // 执行 c.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 C,无重载的 4.转去检查类 C 的父类 B,有重载的 5.执行父类 B 中的 Func 方法 5.输 出结果 Func In B d.Func(); // 执行 d.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 D,无重载的(这个地方要注意了,虽然 D 里有实现 Func(),但没有使用 override 关键 字,所以不会被认为是重载) 4.转去检查类 D 的父类 A,就为本身 5.执行父类 A 中的 Func 方法 5.输出结果 Func In A D d1 = new D(); d1.Func(); // 执行 D 类里的 Func(),输出结果 Func In D Console.ReadLine(); } } } 3.

c++ virtual修饰析构函数

c++ virtual修饰析构函数

C++中的virtual修饰析构函数1. 概述在C++中,虚析构函数是一种特殊的析构函数,可以让派生类对象在被删除时,能够适当地调用其基类析构函数。

这种机制可以实现多态性,对于构建一个多态的对象继承体系是非常有用的。

2. 什么是虚析构函数虚析构函数是在基类中将析构函数声明为虚函数。

在C++中,虚析构函数要求在基类中用virtual关键字声明,在派生类中重写。

这样可以确保当基类的指针指向派生类的对象时,能够正确地调用派生类的析构函数。

3. 虚析构函数的作用虚析构函数的作用是解决当基类指针指向派生类对象时,只调用基类析构函数的问题。

如果不使用虚析构函数,当通过基类指针来删除继承类的对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,导致可能会出现资源泄漏或者未能正确释放资源的问题。

4. 虚析构函数的声明方式虚析构函数只需在基类中进行声明即可,在派生类中无需再次声明为虚函数。

虚析构函数的声明方式如下所示:```cppclass Base {public:virtual ~Base() {// 析构函数的实现}};```5. 虚析构函数的示例下面是虚析构函数的一个简单示例:```cppclass Base {public:Base() {std::cout << "Base constructor" << std::endl; }virtual ~Base() {std::cout << "Base destructor" << std::endl; }};class Derived : public Base {public:Derived() {std::cout << "Derived constructor" << std::endl;}~Derived() {std::cout << "Derived destructor" << std::endl;}};int m本人n() {Base* ptr = new Derived();delete ptr;return 0;}```在上面的示例中,基类Base中声明了虚析构函数,而派生类Derived 中覆盖了基类的析构函数。

C#继承的实现方式

C#继承的实现方式

C#继承的实现⽅式虚⽅法:如果要在派⽣类中继承⽅法或属性,那么就必须在基类中将该属性声明为virtual。

⽅法或属性在默认情况下是不虚拟的,所以如果不在基类中显⽰声明,在派⽣类中⽤override重写该⽅法时就会报错。

当然,如果在派⽣类中⽤new来隐藏基类⽅法也没有问题。

我们看下⾯⼀个例⼦:public class A{public void MethodF(){Console.WriteLine("A.F");}public virtual void MethodG(){Console.WriteLine("A.G");}}public class B : A{new public void MethodF(){Console.WriteLine("B.F");}public override void MethodG(){Console.WriteLine("B.G");}}class Test{static void Main(){B b;b = new B();A a = b;a.MethodF();b.MethodF();a.MethodG();b.MethodG();}它的结果是:A.FB.F B.G B.G也许你会很奇怪为什么a.methodG()的结果为什么是B.G。

那是因为A的methodG⽅法是虚⽅法,如果有派⽣类继承了这个⽅法,那么它就会⾸先去找这个派⽣类的继承⽅法并实现它。

隐藏⽅法:通常是在不重写基类⽅法,但⼜需要再派⽣类中创建⼀个相同⽅法时使⽤。

只要在派⽣类⽅法的前⾯加⼀个new就能实现对基类⽅法的隐藏。

如:class BaseClass{public int Add(int a, int b) {return a + b + 1;}}class Test1 : BaseClass{new public int Add(int a,int b){return a + b;}}如果new⼀个基类,则调⽤Add⽅法时调的是基类的Add⽅法。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C++的虚函数和继承是面向对象编程中非常重要的概念,虚函数通过继承可以实现多态性,为程序设计提供了很大的灵活性。

本文将通过介绍C++中虚函数和继承的特点,结合具体的实现例子来说明其重要性和实际应用。

1. 虚函数的概念与作用
虚函数是C++中面向对象编程的重要概念,它允许在派生类中重写基类的函数,实现多态性。

在基类中使用virtual关键字声明函数为虚函数,在派生类中使用override关键字来标识该函数是对基类虚函数的重写。

虚函数的作用在于:
- 实现运行时多态性:通过基类指针指向派生类对象,可以根据实际对象的类型来调用相应的函数,实现了动态绑定。

- 可以实现接口多态性:通过虚函数可以定义统一的接口,在不同的派生类中按照自己的需求来实现具体的功能。

2. 继承的概念与特点
继承是C++面向对象编程中实现代码重用和扩展的重要机制,通过继承可以创建新类并使用现有类的所有属性和方法。

在C++中,类的继承有公有继承、保护继承和私有继承三种方式,其中公有继承最为常用。

在派生类中可以访问基类的公有成员,但无法访问基类的私有成员。

继承的特点包括:
- 实现代码重用:通过继承可以直接使用基类中已有的属性和方法,减少了重复编写代码的工作量。

- 实现代码扩展:通过在派生类中添加新的属性和方法,可以扩展基类的功能,实现更加灵活的代码设计。

3. 虚函数继承实现例子
接下来将通过一个具体的实现例子来说明虚函数和继承的重要性和实际应用。

假设有一个基类Animal,其中定义了一个虚函数speak()用于输出动物的叫声,然后有两个派生类Dog和Cat,分别重写了speak()函数以实现不同的叫声输出。

示例代码如下:
```cpp
#include <iostream>
class Animal {
public:
virtual void speak() {
std::cout << "Animal speaking..." << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Dog barking..." << std::endl; }
};
class Cat : public Animal {
public:
void speak() override {
std::cout << "Cat meowing..." << std::endl; }
};
int m本人n() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->speak(); // 输出Dog barking...
animal2->speak(); // 输出Cat meowing...
delete animal1;
delete animal2;
return 0;
}
```
在上面的示例中,通过虚函数和继承的结合使用,实现了运行时多态性。

在m本人n函数中,创建了一个指向Dog和Cat对象的基类指针,通过调用speak()函数分别输出了对应的叫声,实现了动态绑定。

通过以上实例,可以看到虚函数和继承的重要性和实际应用。

通过合理的设计和使用,可以让程序结构更加清晰、灵活,并且能够更好地适应未来的需求变化。

总结
本文通过介绍了C++中虚函数和继承的概念和特点,并通过一个实例说明了虚函数和继承的作用和实际应用。

虚函数和继承是面向对象编程中非常重要的特性,合理使用它们可以让程序更加清晰、灵活,并且能够更好地适应未来的需求变化。

希望本文的介绍和实例对读者有所帮助,能够更好地理解和应用虚函数和继承。

相关文档
最新文档