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;
}

在这里插入图片描述
在这里插入图片描述
八、总结

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的多态(Polymorphism)是指在父类和子类之间的相互转换,以及在不同对象之间的相互转换。 C++中的多态性有两种:静态多态和动态多态。 1. 静态多态 静态多态是指在编译时就已经确定了函数的调用,也称为编译时多态C++中实现静态多态的方式主要有函数重载和运算符重载。 函数重载是指在同一作用域内定义多个同名函数,但它们的参数列表不同。编译器根据传递给函数的参数类型和数量来确定调用哪个函数。例如: ```c++ void print(int num) { std::cout << "This is an integer: " << num << std::endl; } void print(double num) { std::cout << "This is a double: " << num << std::endl; } int main() { int a = 10; double b = 3.14; print(a); // 调用第一个print函数 print(b); // 调用第二个print函数 } ``` 运算符重载是指对C++中的运算符进行重新定义,使其能够用于自定义的数据类型。例如: ```c++ class Complex { public: Complex(double real, double imag) : m_real(real), m_imag(imag) {} Complex operator+(const Complex& other) const { return Complex(m_real + other.m_real, m_imag + other.m_imag); } private: double m_real; double m_imag; }; int main() { Complex a(1.0, 2.0); Complex b(3.0, 4.0); Complex c = a + b; // 调用Complex类中重载的+运算符 } ``` 2. 动态多态 动态多态是指在运行时根据对象的实际类型来确定调用哪个函数,也称为运行时多态C++中实现动态多态的方式主要有虚函数和纯虚函数。 虚函数是在父类中定义的可以被子类重写的函数,使用virtual关键字声明。当一个对象的指针或引用指向一个子类对象时,调用虚函数时会根据实际的对象类型来确定调用哪个函数。例如: ```c++ class Shape { public: virtual void draw() { std::cout << "Drawing a shape." << std::endl; } }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ``` 纯虚函数是在父类中定义的没有实现的虚函数,使用纯虚函数声明(如virtual void func() = 0;)。父类中包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能作为基类来派生子类。子类必须实现父类的纯虚函数才能实例化。例如: ```c++ class Shape { public: virtual void draw() = 0; }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值