C++ 学习笔记(2) 虚函数的使用 virtual

虚函数,是为了实现C++运行时的多态,而产生的。还有编译时的动态,那是重载实现的。

特点

1. 通过虚函数机制,子类可以重写基类的虚函数,同名的函数,在不同的子类中有多重实现。

2. 基类可以通过指针,引用的方式,动态绑定子类,然后调用子类重写的虚函数(不是重写的函数不可以调用)。

3. 基类中含有一个vtable 指针,指向一张表 —— 虚函数表,简称 “虚表” 。每个含有虚函数的类中都保存着一个指向虚表的指针, 而虚表中保存了该类各个虚函数的地址。程序会找到子类的虚函数表,然后调用子类的函数。

4.  核心理念就是通过基类访问派生类定义的函数。


虚函数实现的多态必须要借助于指针

因为一个基类的指针可以指向子类的内存空间,一个基类的实例却不可以指向子类的内存空间,因为一个对象实例的内存空间也是确定的,只能调用这块内存空间的函数 ,指针就不同了,指针可以指向其他内存空间。就是说, 虚函数只能通过 -> 来实现动态,不可以通过 . 来实现动态。

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () {
		cout << "Retangle" << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh ptr = One ;   // 基类对象的实例 = 子类对象的实例
	ptr.draw () ;
	return 0 ;
}
运行结果


可见,直接用基类对象实例,访问不到子类对应的虚函数,是不能实现多态的。

指针的实现

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () {
		cout << "Retangle" << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh *ptr = &One ; // ptr 取One 的地址, 指向 One 的内存空间
	ptr->draw () ;
	return 0 ;
}
运行结果



虚函数实现的多态可以借助引用实现

引用 & ,内部的机理是依靠指针实现的,所以这不和“虚函数只能借助于指针实现”矛盾。
引用的实现例子:
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () {
		cout << "Retangle" << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh &ptr = One ;
	ptr.draw () ;
	return 0 ;
}
运行结果


构造函数不可以是虚函数

因为 vtable 是存储在对象中的,对象还没实例化之前,内存空间尚未分配,找不到 vtable,虚函数表还没有被初始化。所以构造函数不可以是虚函数。
而且,对象在构造的过程中,编译器还不知道对象的实际类型,是基类?还是基类的派生类?无法进行虚函数行为。
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual Gragh () {}               // 无参数构造函数  编译错误
	virtual Gragh ( int _data ) {}    // 带参数构造函数  编译错误
	virtual void draw () {
		cout << "基类的虚函数 draw " << endl << endl ;
	}
} ;

class Point : public Gragh {
public:
	void draw () {      
		cout << "子类的虚函数 draw" << endl << endl ; 
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ;

	return 0 ;
}
以上编译不通过的。

静态成员函数不可以是虚函数

静态成员函数和普通的成员函数不同,静态成员函数是属于这个类的,该类的任何一个对象都可以调用这个静态成员函数,但静态成员函数不属于这个类的任何一个对象,所以静态成员函数没有 this 指针,没有对象的引用
虚函数是根据对象的类型来调用的,是建立在对象的基础上的,静态成员函数却是独立于对象而存在的,所以,静态成员函数不可以声明为虚函数。
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual static void draw () {   // 编译报错
		cout << "基类的虚函数 draw " << endl << endl ;
	}
} ;

class Point : public Gragh {
public:
	void draw () {      
		cout << "子类的虚函数 draw" << endl << endl ; 
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ;

	return 0 ;
}
以上编译不通过

内联函数 ( inline ) 不可以是虚函数

内联函数,包括普通函数前加 inline 声明,类体外成员函数前加 Inline, 和类体内成员函数默认为 Inline 。inline 可以理解为一种函数形式的 define , 编译器在编译期间会把Inline 函数调用的地方展开,替换成 Inline 函数的函数体,可以减少函数调用的开销(增加了空间开销)。
在类体内可以直接写 virtual ?

1. inline 函数发生在编译期间,而 virtual 函数行为发生在运行期间(要根据对象类型来调用虚函数),这两者按理是不兼容的。

2. virtual 函数需要函数地址,inline 函数没有地址,这两者冲突的。

但是,平时却可以直接在类体内函数,直接加上 virtual,编译器也不报错  。
虽然是默认内联 inline , 甚至加上 Inline 声明,还是可以写上 virtual , 为了方便的时候可以写写。
原因在于:函数是不是 inline 是由编译器来决定的,类体内函数,即使加上 inline ,编译器有自己的法则,当函数声明为虚函数 virtual 时,编译器会忽略内置的 Inline,  这时inline 不起作用。
所以,在正规的程序中,要设置为  virtual 的函数,最好把函数体写在类定义之外。

虚函数的访问权限可以是子类(派生类)的 public , protected , private 中,对应虚函数的函数

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << endl << "基类的虚函数 draw " << endl << endl ;
	}
} ;

class Point : public Gragh {
public:
	virtual void draw ( int ans ) {      
		cout << endl << "子类的虚函数 draw" << endl << endl ; 
	}
protected:
	virtual void draw () {
		cout << endl << "子类的 protected 函数被基类访问了" << endl << endl ;
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ;

	return 0 ;
}

运行结果


#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << endl << "基类的虚函数 draw " << endl << endl ;
	}
} ;

class Point : public Gragh {
public:
	virtual void draw ( int ans ) {      
		cout << endl << "子类的虚函数 draw" << endl << endl ; 
	}
private:
	virtual void draw () {
		cout << endl << "子类的 private 函数被基类访问了" << endl << endl ;
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ;

	return 0 ;
}
  运行结果

很奇怪,基类指针通过虚函数,竟然可以访问子类的 private 函数(重写的虚函数),可能是因为虚函数表只根据对象类型来选择虚函数,而不看是不是 private,绕过了C++的 private 访问权限。


子类重写的虚函数,参数·返回类型·函数类型  要和基类相同

虚函数只有函数返回类型,形参个数,形参类型都相同,才能有统一的接口。

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << "基类的虚函数 draw " << endl << endl ;
	}
} ;

class Point : public Gragh {
public:
	virtual void draw ( int ans ) {      // 函数名相同,但参数不同
		cout << "子类的虚函数 draw" << endl << endl ; 
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ;

	return 0 ;
}
运行结果

参数不同,基类不会调用子类的同名虚函数。

而且还要注意 const 和引用限定符的干扰,const 声明的虚函数,尽管参数相同,也不会被非 const 基类调用。


虚函数:override 

override 是 C++11 新添加的特性。
特点:

1. 只能放在派生类的虚函数声明中,只能存在于派生类,而且不可以写在类定义外。

2. 如果派生类(子类)确定是重写了基类中的某个虚函数,最好显式地加上 override, 让编译器去检查子类重写的虚函数和基类虚函数的一致性,包括参数,函数名,const , 引用限定等。

3. 目的是检查虚函数是否被重写了 | 基类虚函数是否被修改了

简单例子:
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {   
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () override {                    // virtual 可加可不加        
		cout << endl << "Retangle" << endl << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh *ptr = &One ;
	ptr->draw () ;
	return 0 ;
}
这里,子类的 draw 函数明确声明了自己是重写了基类的 draw 虚函数。
运行结果


面向对象编程的过程中,很容易为了添加功能,而修改了基类虚函数的某些参数,或者是函数类型,这时候子类重写的虚函数就不是重写了,而是变成了子类自定义的虚函数,基类指针也不可以通过虚函数调用子类重写的函数,这样一来就很危险。
所以,如果子类确实重写了基类的虚函数,最好加上 override ,可以作为一致性检查 。
class Gragh {
public:
	virtual void draw () {   
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw ( int arg = 1 ) override {        // override 检查, 参数不一致
		cout << endl << "Retangle" << endl << endl ;
	}
} ;
或者是
class Gragh {
public:
	virtual void draw ( int arg = 1 ) {    // 基类的虚函数的参数被修改
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {  
public:
	virtual void draw () override {                   // override 检查到,基类不存在要重写的虚函数
		cout << endl << "Retangle" << endl << endl ;  
	}
} ;
所以,虚函数之间的覆盖,继承必须要达到高度的一致,参数,函数名,函数类型等等。
override 只是提供了一种检查。


虚函数:final 

final 是 C++11 的新特性。
特点:

1. 防止派生类重写基类的虚函数,( 重写点到为止 )

简单例子:
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw ( ) final {   // 明确了子类不可以重写基类的虚函数
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () override {                 // override 有没有,都会报错
		cout << endl << "Retangle" << endl << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh *ptr = &One ;
	ptr->draw () ;
	return 0 ;
}
编译通不过。

但是,如果函数不一致,例如下面
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () final {   
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw ( int arg = 1 ) {                    // final 对这个不起作用
		cout << endl << "Retangle" << endl << endl ;
	}
} ;

int main () {
	Retangle One ;
	Gragh *ptr = &One ;
	ptr->draw () ;
	return 0 ;
}
这个是可以运行的,子类中函数参数和基类虚函数不一致,不受 final 的制约。




基类指针不可以访问子类自定义的成员变量和自定义的成员函数

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << "ans = 1 " << endl ;
	}
} ;

class Point : public Gragh {
public:
	int child_member ;
	Point () {
		child_member = 100 ;
	}
	// 子类中对应的函数可以不写 virtual, 最好写上, 增强可读性
	// 如果希望子类的子类不再重写这个函数, 可以写成 virtual void draw () override final // C++11 支持
	virtual void draw () {      
		cout << "ans = 2" << endl ;
	}
} ;

int main () {

	Point Four ;

	Gragh *C = &Four ;

	C->draw () ; // 正确
	
    cout << "child_member = " << C->child_member << endl ; 

    // C->child_member 错误, 基类指针, 指向子类对象, 可以通过虚函数访问子类成员函数
	// 但是不可以访问子类自己定义的成员变量
	// 也不可以访问子类的函数 ( 和基类(虚) 函数不同名 | 同名却参数不同  )

	return 0 ;
}
以上编译错误。
child_member 是子类自定义的成员变量, 基类指针只能通过虚函数机制调用子类中符合要求的函数,不可以访问子类自定义的成员变量,和自定义的成员函数。

纯虚函数

如果基类的某个虚函数声明上加上 = 0 , 就是纯虚函数。

特点

1. 带有纯虚函数的类是抽象类,只能被继承,不可以声明实例,因为纯虚函数只有声明,没有定义,实例不知道纯虚函数的具体实现(具体实现在子类)。

2. 纯虚函数只是提供子类(派生类)的接口,要在子类中重写该函数的实现。

3. 纯虚函数可以写函数体,但是必须写在类定义外,不能写在类定义内。不过纯虚函数本来就是只提供一个接口,不提供实现,所以,纯虚函数一般不写函数实现,即使写了,子类也没有继承的必要,因为这样一来,纯虚函数也没有 “纯虚” 的意义。如果偏要写,子类调用这个要显式调用,例如 Gragh::draw

4. 如果子类对象要实例化,纯虚函数在子类中一定要有实现。因为如果纯虚函数在子类中没有实现,子类的虚函数表中记录的那个虚函数地址是基类(父类)的纯虚函数的地址,这样一来,子类也带有一个纯虚函数,子类也成了抽象类,这时候,子类不可以有实例。但是抽象类的子类也可以是抽象类,只是其对象不能实例化。

例子:解释 1 ,2 ,3
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () = 0 ;  // 纯虚函数
} ;

void Gragh::draw () {              // 偏要写, 要写在类之外; 根本没必要写函数体
	cout << "Gragh" << endl ;  // 虽然编译器允许, 但不要这样写 
}

class Retangle : public Gragh {
public:
	virtual void draw () {
		cout << "Retangle" << endl ;
	}
} ;

int main () { 
	Retangle One ;        // Gragh 不能声明实例
	Gragh *ptr = &One ;
	ptr->draw () ;
	return 0 ;
}
例子2:解释 4
#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () = 0 ;  // 会报错
} ;

class Retangle : public Gragh {
public:
	// virtual void draw () {
	// 	cout << "Retangle" << endl ;
	// }
} ;

int main () {
	Retangle One ;   // 会报错, 因为 Retangle 继承了 Gragh 的纯虚函数, 没有实现
	Gragh *ptr = &One ;    // Retangle 也成了一个抽象类, 不可以有实例
	ptr->draw () ;
	return 0 ;
}

纯虚函数和虚函数的区别 :

1. 纯虚函数定义了一个统一的接口,具体全部靠子类重写去实现。

2. 虚函数不仅定义了一个统一的接口,供子类重写,覆盖掉原来的虚函数,以达到多种不同的实现;还为一些没重写该虚函数的子类提供了统一的继承,也就是默认的函数行为,如果子类自己不声明这个函数,就默认用父类的虚函数。

3. 纯虚函数的意义,不仅仅是提供了一个统一的接口,更重要的是规范子类的行为,强制要求所有继承它的派生类,如果其对象要实例化就必须具体实现这个纯虚函数 ,所以说纯虚函数通过强制子类重写虚函数,比普通的虚函数更容易实现多态 。


如果现在,有一个这样的基类,内部有一个虚函数,这个虚函数不是纯虚函数,有函数定义,也有函数体,但是函数体是空的,也就是基类虚函数没有任何操作,如下:

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {}
} ;

int main () {
	Gragh One ;
	One.draw () ;
	return 0 ;
}

编译是通过的。


上面这个和带有纯虚函数的类有共同点:

 基类虚函数都没有具体实现,都提供了一个统一的接口,供子类重写虚函数,以达到多态。

class Gragh {
public:
	virtual void draw () {}     
} ;

class Gragh {
public:
	virtual void draw () = 0 ;
} ;
也有很多不同点:

1. 上面的虽然虚函数没有具体实现,但是虚函数既有声明,又有函数体,其对象可以被实例化

    下面的是纯虚函数,只有声明,没有函数体,必须要靠子类重写,其对象不可以被实例化

2. 下面的纯虚函数更容易做到多态!

    因为如果子类要实例化对象,纯虚函数会强制继承它的子类重写它,以此达到多态,比较规范。

    但是,上面的不会强制其子类重写它。如果子类中忘记重写函数,子类就会直接继承基类的虚函数,子类的功能容易缺失,而且编译不报错,不好找错。

虚函数:动态绑定

静态绑定:编译时,根据声明的类型调用函数 

动态绑定:运行时,根据对象真实类型调用函数,需要 virtual

可以通过虚函数 virtual 来实现动态绑定。

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () {
		cout << endl << "基类  Gragh " << endl << endl ;
	}
	void normal () {
		cout << endl << "基类 Gragh 的 normal " << endl << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () {
		cout << endl << "子类  Retangle " << endl << endl ;
	}
	void normal () {
		cout << endl << "子类 Retangle 的 normal " << endl << endl ;
	}
} ;

int main () {
	Retangle One ;          
                           // ptr 是基类 Gragh 指针
	Gragh *ptr = &One ;    // ptr 的静态类型是 Gragh , 动态类型是 Retangle

	ptr->draw () ;         // draw 函数在 Gragh 是虚函数, 动态绑定, 按照实际指向的对象( 内存空间 ) 来调用函数

	ptr->normal () ;       // normal 函数在 Gragh 不是虚函数, 静态绑定, 按照声明的对象类型来调用函数

    ptr = NULL ;
	return 0 ;
}

运行结果


动态绑定,通俗地理解,就是 virtual 使得基类指针,可以调用子类中重写的虚函数。实质就是,如果子类重写了基类的虚函数,子类重写的虚函数覆盖了基类的虚函数,子类的虚函数表上原来存储的是基类虚函数的地址,重写之后,子类的虚函数表存储的就是子类自己写的这个虚函数的地址了。但,如果子类没有重写基类的虚函数,子类的虚函数表上存储的还是基类虚函数的地址。

如果在上个例子中继续加入一个子类, 改变 ptr 指向的子类内存,ptr 就可以根据实际指向的对象类型,调用对应类的函数。这也就解释了,为什么虚函数只能通过指针来实现,因为指针可以动态指向一块内存。

如果 ptr 原来就是 Retangle * 类型,就直接调用 Retangle 的函数了(Retangle 有子类另外)。


虚函数和缺省参数

缺省参数,可以理解为,如果函数调用处不传入参数,函数会传入默认的一个参数。缺省参数是静态绑定的,即使子类继承自父类,利用虚函数的动态绑定机制,缺省参数依旧是静态的。

举个例子:

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw ( int arg = 1 ) {
		cout << endl << "参数  :  " << arg << endl << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw ( int arg = 2 ) {
		cout << endl << "参数  :  " << arg << endl << endl ;
		cout << "Retangle" << endl << endl ;
	}
} ;

int main () {
	Retangle One ;

	Gragh *ptr = &One ;    // ptr 静态类型是 Gragh , 动态类型是 Retangle

	Retangle *qtr = &One ; // qtr 静态类型是 Retabgle , 动态类型也是 Retangle

	ptr->draw () ;         // 缺省参数 = 1, 运行时调用的是动态类型 Retangle 的 draw

	qtr->draw () ;         // 缺省参数 = 2, 运行时调用的是动态类型 Retangle 的 draw

	ptr = qtr = NULL ;
	return 0 ;
}

运行结果


两个都调用的是 Retangle类 的 draw 函数, 但是缺省参数却不同。


虚函数和 const 

在成员函数后加上 const , 意为该函数不可修改其数据成员( mutable 除外 ) 。虚函数上加上 const , 也就是加上了 const 的限制,虚函数其他用法依旧。

不过,要注意几点

1. 基类虚函数是 const , 子类可以直接继承,但是如果要重写该常量函数,也必须是 const ;
    基类指针如果要调用 const 函数, 也必须是 const 

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () const {
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () const {        // 必须加上 const 
		cout << "Retangle" << endl ;
	}
} ;

int main () {
	const Gragh *ptr = new Retangle ;   // 基类指针也必须是 const 
	ptr->draw () ; 
	ptr = NULL ;
	return 0 ;
}

运行结果


如果基类指针不调用 const 函数,就不能加 const ,即使基类中存在 const 类的虚函数,如下:

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () const {
		cout << "Gragh" << endl ;
	}
	void init () {
		cout << endl << "基类的 not const 普通成员函数" << endl ;
	}
	virtual void other () {
		cout << endl << "基类的 not const 虚函数" << endl << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () const {
		cout << endl << "Retangle" << endl << endl ;
	}
	virtual void other () {
		cout << endl << "子类的 not const 虚函数" << endl << endl ;
	}
} ;

int main () {
	Gragh *ptr = new Retangle ;

	ptr->init () ;     // 访问基类非 const 成员

	ptr->other () ;    // 基类指针访问子类的非 const 虚函数

	ptr = NULL ;
	return 0 ;
}
运行结果:



2. 如果基类虚函数是 const , 那么基类指针通过虚函数实现多态时,会调用子类中的 const 函数,不会调用非 const 函数

// 2. 如果基类虚函数是 const , 那么基类指针通过虚函数实现多态时,会调用子类中的 const 函数,不会调用非 const 函数

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () const {
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	virtual void draw () const {                         // 根据 const 特性, 找覆盖原虚函数的 const 函数
		cout << endl << "子类 const 的 draw 函数" << endl << endl ;
	} 
	virtual void draw () {                               // 而不会找非 const 的函数
		cout << endl << "子类 Not const 的 draw 函数" << endl << endl ;
	}
	// void draw () {                                    // 无论是不是虚函数, 只要非 const, 就不会被 const 的基类调用
	// 	cout << "子类 Not const 的 draw 函数" ;
	// }
} ;

int main () {
	const Gragh *ptr = new Retangle ;   // 基类指针也必须是 const 
	ptr->draw () ; 
	ptr = NULL ;
	return 0 ;
}

运行结果


但是,如果把 Gragh *ptr 声明为非 const 基类指针,调用的就会是非 const 的子类虚函数了。

也说明了一点, const 可以实现函数的重载 ! 同名,同参数的函数,因为 const 产生了差异,实现多态。


纯虚函数和 const 

如果不改变虚函数, 经常表示成 const = 0 ,其实 const 和 = 0 没关系,const 表示不可修改, = 0 表示是纯虚函数。

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	virtual void draw () const = 0 ;
} ;

class Retangle : public Gragh {
public:
	virtual void draw () const {    // 子类重写 const 函数
		cout << endl << "Retangle" << endl << endl ;
	}
} ;

int main () {
	Gragh *ptr = new Retangle ;
	ptr->draw () ;
	ptr = NULL ;
	return 0 ;
}
运行结果




虚函数:基类作为统一的函数参数

用统一的基类指针,代表不同的子类作为函数参数,简单明了。

#include <bits/stdc++.h>
using namespace std ;

class Gragh {
public:
	int inches ;
	virtual void draw () {    
		cout << "Gragh" << endl ;
	}
} ;

class Retangle : public Gragh {
public:
	Retangle () {
		inches = 200 ;
	}
	virtual void draw () {
		cout << "Retangle" ;
	}
} ;

class Square : public Gragh {
public:
	Square () {
		inches = 100 ;
	}
	virtual void draw () {
		cout << "Square" ;
	}
} ;

void Combine ( Gragh *a , Gragh *b ) {
	a->draw () ;
	cout << " + " ;
	b->draw () ;
	cout << " = " << a->inches + b->inches << endl << endl ;
}

int main () {
	Gragh *a = new Retangle ;
	Gragh *b = new Square ;
	Combine ( a , b ) ;
	return 0 ;
}
运行结果



虚函数与析构函数 


猜你喜欢

转载自blog.csdn.net/nishisiyuetian/article/details/79666523
今日推荐