c++多态总结

1、C++中通过virtual关键字对多态进行支持,使用virtual声明的函数被重写后即可展现多态特性。
注意和虚继承的区别。虽然用的是同一个关键字,但意义完全不同。


2、多态的意义:多态可以使用未来,80年代写了一个框架,可以调用90年代写的代码。

多态是设计模式的基础,多态是框架的基础。


3、多态成立的条件

   3.1 要有继承
   3.2 要有虚函数重写
   3.3 要有父类指针(父类引用)指向子类对象
 


4、演示代码:

class Parent
{
public:
	Parent(int a)
	{
		this->a = a;
	}
	virtual void print() //给一个成员函数 定义为 虚函数。
	{
		cout << "Parent ::print() : a = " << a << endl;
	}
private:
	int a;
};


class Child :public Parent
{
public:
	Child(int a, int b) :Parent(a)
	{
		this->b = b;
	}

	//重定义父类函数: 发生在子类和父类之间
	//当子类重写父类的成员函数,如果父类中这个函数不是虚函数, 是函数的重定义
	//当子类重写父类的成员函数,如果父类中这个函数是  虚函数。 是函数的重写。
	virtual void print()
	{
		cout << "Child: print b:" << b << endl;
		//Parent::print();
	}
private:
	int b;
};

void myPrintFunc(Parent *p) //  让父类指针指向子类对象的时候,
{
	p->print();// 再此时 print函数 发生了多态现象
}

int main(void)
{
	Child c(10, 20);
	c.print(); //调用的子类的print

	Parent p(100);
	p.print(); //调用的父类的print

	cout << "  -----  " << endl;
	myPrintFunc(&p); //希望调用父类的print

	cout << " ------  " << endl;
	myPrintFunc(&c); //希望调用子类的print  
	
	return 0;
}

5、析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象 。
 

class A
{
public:
	A() {
		cout << "A() ..." << endl;
		this->p = new char[64]; //给p开辟了一个空间
		memset(p, 0, 64);
		strcpy(p, "A string ");
	}

	virtual void print()
	{
		cout << p << endl;
	}

	virtual ~A()
	{
		cout << "~A()...." << endl;
		if (p != NULL) {
			delete[] p;
			p = NULL;
		}
	}
private:
	char *p; 
};

class B : public A
{
public:
	B() {
		cout << "B()..." << endl;
		this->p = new char[64];
		memset(p, 0, 64);
		strcpy(p, "B string");
	}

	//重写
	virtual void print()
	{
		cout << p << endl;
	}

	virtual  ~B()
	{
		cout << "~B()...." << endl;
		if (p != NULL) {
			delete[] p;
			p = NULL;
		}
	}
private:
	char *p;//和A类的p一样,都是个字的私有成员
};
void func(A *p)//p = cp;  p = &c //用父类指针指向子类对象。
{ 
	p->print(); //在此处发生多态。
}

void myDelete(A*p) //p->cp   父类指针指向子类对象
{
	delete p; //p->~()  //如果~() 不加virtual关键 不会发生多态。
    //此时如果传入B,会先析构B再析构A
}

6、多态的实现原理:
    6.1 当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储类 成员函数指针 的数据结构,是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中。
    
    6.2 存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。

    6.3 通过虚函数表,指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。    而普通成员函数是在编译时就确定了调用的函数。在效率上, 虚函数的效率要低很多。出于效率考虑,没有必要将所有成员函数都声明为虚函数。

    6.4 一个类中不管有几个虚函数,vptr指针只会存在一个。


7、构造函数中能否调用虚函数,实现多态??
不能,因为对象在创建的时,由编译器对VPTR指针进行初始化,只有当对象的构造完全结束后VPTR的指向才最终确定。
父类对象的VPTR指向父类虚函数表,子类对象的VPTR指向子类虚函数表。


8、纯虚函数:
    8.1 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本。

    8.2 纯虚函数不需要定义,只需要申明即可。

    8.3 纯虚函数的语法:virtual 类型 函数名(参数表) = 0;

    8.4 含有纯虚函数的类,称为抽象基类,不可实列化。 即不能创建对象,存在的意义就是被继承,提供族类的公共接口。

    8.5 如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,

    则该虚函数在派生类中仍然为纯虚函数,派生类仍然为纯虚基类。


9、纯虚函数相关代码展示:

//图形类
//拥有纯虚函数的类, 就叫抽象类
class Shape
{
public:
	//是一个抽象的接口,说明图形是有一个得到面积方法
	virtual  double getArea() = 0;//代表一个接口,一个求图形面积的接口

	//定义一个个打印面积的接口
	virtual void print() = 0;
};

//圆类
//如果 一个子类继承了抽象类, 那么一定要重写这个纯虚函数。
class Circle :public Shape
{
public:
	Circle(double r)
	{
		this->r = r;
	}

	//重写父类抽象类的纯虚函数
	virtual double getArea() 
	{
		return 3.14 *  r * r;
	}

	
	virtual void print() {
		cout << "圆的面积是" << endl;
		cout << this->getArea() << endl;
	}
	
private:
	double r;//半径
};


//实现一个正方形
class Rect :public Shape
{
public:
	Rect(double a)
	{
		this->a = a;
	}
	//是一个抽象的接口,说明图形是有一个得到面积方法
	virtual  double getArea()
	{
		return a*a;
	}

	//顶一个打印面积的接口
	virtual void print()  {
		cout << "正方形的面积是" << endl;
		cout << this->getArea() << endl;
	}
private:
	double a;//边长
};


//一个传递抽象类 指针的架构函数
void printArea(Shape *p)
{
	p->print();
}

//业务层
int main(void)
{
	//Shape p;//抽象类不能够实例化

	Shape *sp = new Circle(10.0);

	//抽象类的指针就可以调用纯虚函数, 接口

	printArea(sp);

	delete sp;

	//创建一个正方形的对象。用抽象类指针(父类指针)指向子类对象
	Shape *sp1 = new Rect(10.0);

	printArea(sp1);
	delete sp1;

	cout << " ------   " << endl;

	return 0;
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值