C++多态

Posted 蚍蜉撼树谈何易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++多态相关的知识,希望对你有一定的参考价值。

一、多态的概念?
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态
二、多态的条件?

三、什么是重写?
派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数
条件:
1.基类的成员函数必须是虚函数
2.子类的成员函数可以不加virtual关键字,但一般建议加上
3.基类和子类的虚函数原型必须一致,(返回值类型,函数名字,参数列表),有两个例外,返回值为类型指针或引用时可以,第二个是析构函数。
4,重写与访问权限无关。

class Base
{
public:
	virtual void f1()
	{
		cout << "Base::f1()" << endl;
	}

	virtual void f2()
	{
		cout << "Base::f2()" << endl;
	}

	void f3()
	{
		cout << "Base::f3()" << endl;
	}

	virtual void f4()
	{
		cout << "Base::f4()" << endl;
	}

	virtual void f5()
	{
		cout << "Base::f5()" << endl;
	}

	//virtual void f6()
	//{
	//	cout << "Base::f6()" << endl;
	//}
};

class Derived : public Base
{
public:
	// 与基类f1原型完全相同
	virtual void f1()
	{
		cout << "Derived::f1()" << endl;
	}

	// 与基类f2不同,缺少virtual关键
	void f2()
	{
		cout << "Derived::f2()" << endl;
	}

	// 与基类中f3不同--->原型一致,但是基类f3不是虚函数
	virtual void f3()
	{
		cout << "Derived::f3()" << endl;
	}

	// 与基类虚函数参数列表不同
	virtual void f4(int a)
	{
		cout << "Derived::f4(int)" << endl;
	}

	// 与基类虚函数f6的返回值类型不同
	//virtual int f6()
	//{
	//	cout << "Derived::f6()" << endl;
	//	return 0;
	//}

protected:
	// 与基类f5虚函数的访问权限不同
	virtual void f5()
	{
		cout << "Derived::f5()" << endl;
	}
};


// pb指向一个子类的对象
// 如果重写成功了,调用的一定是子类的方法
void TestVirtual(Base* pb)
{
	pb->f1();
	pb->f2();
	pb->f3();
	pb->f4();
	pb->f5();
	//pb->f6();
}

int main()
{
	Derived d;
	TestVirtual(&d);
	return 0;
}


两种成员函数不同仍旧构成重载的情况?
1.协变:
// 基类虚函数返回基类的指针或者引用
// 子类虚函数返回子类的指针或者引用
// 注意:"基类"虚函数 返回"基类"指针或者引用
// 这两个基类可以不是同一个继承体系

/ 协变:
// 基类虚函数返回基类的指针或者引用
// 子类虚函数返回子类的指针或者引用
// 注意:"基类"虚函数    返回"基类"指针或者引用
//     这两个基类可以不是同一个继承体系

class A
{};

class B : public A
{};

class Base
{
public:
	virtual A* f1()
	{
		cout << "Base::f1()" << endl;
		return nullptr;
	}
};

class Derived : public Base
{
public:
	virtual B* f1()
	{
		cout << "Derived::f1()" << endl;
		return nullptr;
	}
};


void TestVirtual(Base& b)
{
	b.f1();
}


int main()
{
	Base b;
	Derived d;

	TestVirtual(b);
	TestVirtual(d);
	


2.析构函数

class Base
{
public:
	Base()
	{
		cout << "Base::Base()" << endl;
	}

	// 基类的析构函数如果设置为虚函数
	// 子类的析构函数只要显式给出,子类的析构函数就会将基类的析构函数重写
	virtual ~Base()
	{
		cout << "Base::~Base()" << endl;
	}
protected:

};


class Derived : public Base
{
public:
	Derived()
		: Base()
		, _d(0)
	{
		p = new int[10];
		cout << "Derived::Derived()" << endl;
	}

	~Derived()
	{
		delete[] p;
		cout << "Derived::~Derived()" << endl;
	}
protected:
	int _d;
	int* p;
};


int main()
{
	Base* pb = new Derived;

	/*
	delete pb;
	   1. 调用析构函数
			子类已经将基类的析构函数重写了,因此调用那个类的析构函数
			就需要看pb实际指向的是那个类的对象
	   2. 调用operator delete(void* p)释放对象的空间
	*/
	delete pb;

	return 0;
}


为什么在继承体系下,一般建议将基类析构函数设置为虚函数? ****
若基类的析构函数不为virtual修饰的话,若子类对象涉及到资源管理的话,此时将子类对象赋值给基类对象,此时若函数调用完成的话,只会调用基类的析构函数,而不会去调用子类的析构函数,这就会造成内存泄露的问题。
四、C++11中的override和final
override:单词含义 重写
作用:在编译阶段,检测子类虚函数是否对基类那个虚函数进行重写了
如果被override修饰的子类虚函数重写了基类的某个虚函数,编译通过
否则编译失败

override:只能修饰子类的虚函数
final:
// 1. 修饰虚函数—表明该虚函数不能被其子类重写了
// final一般用来修饰子类的虚函数,也可以修饰基类,不过没什么意义
// 2. 修饰类—>表明该类不能被继承了
// 需求:C类中的fun1这个虚函数不想被C的子类再重写了

class B
{
public:
	virtual void fun1()final
	{
		cout << "B::fun1()" << endl;
	}
};
class C : public B
{
public:
	//virtual void fun1()//error
	//{
	//	cout << "C::fun1()" << endl;
	//}

	virtual void fun2()
	{
		cout << "C::fun2()" << endl;
	}
};

// 需求:D类要求:我不能再被继承
class D final : public C
{
public:
	//virtual void fun1()
	//{
	//	cout << "D::fun1()" << endl;
	//}

	virtual void fun2()
	{
		cout << "D::fun2()" << endl;
	}
};

class E : public D//error 
{};

五、重载、覆盖(重写)、隐藏(重定义)的对比 ******

六、抽象类 **
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。 (因为无法确定给多大的空间)派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象纯虚函数规范了派生类必须重写,另外 纯虚函数更体现出了接口继承

class Shape
{
public:
	// 纯虚函数
	virtual double CalcCircle() = 0;
	// virtual double CaclArea() = 0;
};


// 三角形
class Triangle : public Shape
{
public:
	Triangle(double a, double b, double c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
	}

	// 获取周长
	virtual double CalcCircle()
	{
		return a + b + c;
	}
private:
	double a;
	double b;
	double c;
};

// 矩形
class Rectangle : public Shape
{
public:
	Rectangle(double l, double w)
		: length(l)
		, width(w)
	{}

	virtual double CalcCircle()
	{
		return 2 * (length + width);
	}
private:
	double length;
	double width;
};


// 子类必须重写抽象类中所有的纯虚函数,如果有一个纯虚函数没有重写
// 则子类也是抽象类
class Circle : public Shape
{
public:
	Circle(double r)
	{
		this->r = r;
	}

	virtual double CalcCircle()
	{
		return 3.14 * r * r;
	}

private:
	double r;
};

void TestShape(Shape& s)
{
	s.CalcCircle();
}


int main()
{
	// Shape s;
	Triangle t(3, 4, 5);
	Rectangle r(2, 3);
	Circle c(2);

	TestShape(t);   //
	TestShape(r);
	TestShape(c);
	return 0;
}

接口继承与实现继承:

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
七、多态原理
7.1什么情况下肯定会生成构造函数?

// 多态的实现原理
// 1. 包含有虚函数的类与不包含虚函数的类有什么区别?
//     >> 带有虚函数的类会多4个字节
//     >> 如果没有显式定义构造函数时,编译器一定会给带有虚函数的类合成构造函数
//     >> 在构造函数中,编译器会给对象的前4个字节赋值
class B
{
public:
	void f()
	{
		cout << "B::f()" << endl;
	}

	int _b;
};


// 多的4个字节是什么作用?
class D
{
public:
	virtual void f1()
	{
		cout << "D::f1()" << endl;
	}

	virtual void f2()
	{
		cout << "D::f2()" << endl;
	}

	virtual void f3()
	{
		cout << "D::f3()" << endl;
	}
	int _d;
};


int main()
{
	cout << sizeof(B) << endl;
	cout << sizeof(D) << endl;


	B  b;
	b._b = 10;

	D  d;
	d._d = 10;
	return 0;
}





class B1
{
public:
	virtual void f1()
	{
		cout << "B1::f1()" << endl;
	}

	virtual void f2()
	{
		cout << "B1::f2()" << endl;
	}

	int _b1;
};

class B2
{
public:
	virtual void f3()
	{
		cout << "B2::f3()" << endl;
	}

	virtual void f4()
	{
		cout << "B2::f4()" << endl;
	}

	int _b2;
};


class D : public B1, public B2
{
public:
	virtual void f1()
	{
		cout << "D::f1()" << endl;
	}

	virtual void f4()
	{
		cout << "D::f4()" << endl;
	}

	virtual  void f5()
	{
		cout << "D::f5()" << endl;
	}

	int _d;
};


typedef void(*PVFT)();

void PrintVFT1(B1& b, const string& s)
{
	cout << s << endl;
	PVFT* pvft = (PVFT*)*(int*)&b;
	while (*pvft)
	{
		(*pvft)();
		++pvft;
	}
	cout << endl;
}

void PrintVFT2(B2& b, const string& s)
{
	cout << s << endl;
	PVFT* pvft = (PVFT*)*(int*)&b;
	while (*pvft)
	{
		(*pvft)();
		++pvft;
	}
	cout << endl;
}

int main()
{
	cout << sizeof(D) << endl;

	D  d;
	d._b1 = 1;
	d._b2 = 2;
	d._d = 3;

	PrintVFT1(d, "D->B1->VFT:");
	PrintVFT2(d, "D->B1->VFT:");
	return 0;
}



八、总结

以上是关于C++多态的主要内容,如果未能解决你的问题,请参考以下文章

C++提高:多态

C++提高:多态

[ C++ ] 多态原理 多态

C++类和对象--多态

这些 C++ 代码片段有啥作用?

C++多态实现原理