C++ 多态

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了
Person。Person对象买票全价,Student对象买票半价


class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
	/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
	为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
	这样使用*/
	/*void BuyTicket() { cout << "买票-半价" << endl; }*/
};
void Func(Person& p)

//虚函数重写的两个例外:
//1. 协变(基类与派生类虚函数返回值类型不同)
//派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
//针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。(了解)
//2. 析构函数的重写(基类与派生类析构函数的名字不同)
//如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,
//都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,
//看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处
//理,编译后析构函数的名称统一处理成destructor。
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

 原理图

 

普通继承是子类继承父类的全部,是一种复用。

虚函数在多态情况下是子类复用父类的接口,重写父类的实现。

 多态条件

 举例(ps:下面就是通过修改多态的条件从而不满足多态)

void Func(Person p)
{
	p.BuyTicket();
}

要求三同,假设我们这里让参数不同:


class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	virtual void BuyTicket(int ) { cout << "买票-半价" << endl; }

};

重写虚函数

Person* p=new Person;
	delete p;
	p = new Student;
	delete p;

我们发现子类的析构函数没有被正确调用。

这是因为delete是根据指针类型来调用的。

但是person指针可能指向person,也可能指向student。

它的调用如下:先调用destrutor函数再调用析构函数。 destrutor是普通函数,普通函数调用看类型,因为是person指针类型,所以调用person析构。

我们不希望它是普通继承,因为如果是普通继承就不会调子类析构,会造成内存泄漏。

那我们怎么样让它变多态呢?第一:三同(参数相同,函数名相同,返回值相同)二:虚函数重写

~Person()    ~Student()参数没有,返回值没有,函数名不同,但是为了满足多态进行了特殊处理。

现在只差虚函数重写这一项了,我们给析构加上virtual:

virtual ~Person()
	{
		cout << "~Person()" << endl;
	}
};

virtual ~Student()
	{
		cout << "~Student()" << endl;
	}

练习题

说出如下代码打印结果:


class Person
{
public:
	virtual void func(int a = 1) { cout << "A->" << a << endl; }
	virtual void test() { func(); }

};
class Student: public	Person
{
	void func(int a = 0) {cout << "B->" << a << endl;}
};

int main()
{
	Student* B=new Student;
	B->test();
	return 0;

}

结果:

B->1

解析:

那如果是这样呢?我不访问test(),我直接访问func*()

class Person
{
public:
	virtual void func(int a = 1) { cout << "A->" << a << endl; }
	virtual void test() { func(); }

};
class Student: public	Person
{
public:
	void func(int a = 0) {cout << "B->" << a << endl;}
};

int main()
{
	Student* B=new Student;
	B->func();
	return 0;

}

 答案很明显,不构成多态,因为多态需要父类指针,而下面是子类的指针,不构成多台那就是普通的,普通的话就会隐藏,会访问子类的。

 要想访问父类我们指定父类域就可以了:

#include <iostream>
using namespace std;

class Person
{
public:
    virtual void func(int a = 1) { cout << "A->" << a << endl; }
    virtual void test() { func(); }
};

class Student : public Person
{
public:
    void func(int a = 0) {
        Person::func(1); // 调用基类Person的func函数
        cout << "B->" << a << endl;
    }
};

int main()
{
    Student* B = new Student;
    B->Person::func();
    return 0;
}

要想形成多态我们改为父类指针就行了:


class Person
{
public:
	virtual void func(int a = 1) { cout << "A->" << a << endl; }
	virtual void test() { func(); }

};
class Student: public	Person
{
public:
	void func(int a = 0) {cout << "B->" << a << endl;}
};

int main()
{
	Person* B = new Person;
          B=new Student;
	B->func();
	return 0;

}

抽象类

虚函数+“=0”就是纯虚函数包含纯虚函数的类就叫抽象类,抽象类不能实例化:


class A
{
public:
	virtual void fun() = 0;

};

 

那抽象类要怎么才能实例化呢?不能实例化这个类就没有意义,实例化要通过虚函数重写:

如下,指针指向谁就访问谁


class A
{
public:
	virtual void fun() = 0;

};

class B: public A
{
	void  fun()
	{
		cout << "B->舒适" << endl;
	}
};
class C : public A
{
	void  fun()
	{
		cout << "B->巴适" << endl;
	}
};

void fun(A* p)
{ 
	p->fun();
}
int main()
{

	fun(new B);
	fun(new C);

	return 0;
}

 

虚函数表

如下代码求出来的字节大小为多少:


class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person {
public:
	 void BuyTicket() { cout << "买票-半价" << endl; }

private:
	int _a;
};


int main()
{
	cout << sizeof(Student) << endl;
	return 0;
}

 答案:

 原因,只要有虚函数,大小就要把多算一个虚函数指针的大小,如果没有虚函数,那它的大小就为它的成员int_a的大小,就是4:

 虚函数指针存放在 虚函数表里面。虚函数表我们称为vf

ptr(virtaul function  ptr  table ),而vfptr在代码段里存放着。

 现在在子类中再加个char _b成员变量,然后在64位操作系统下运行,此刻的大小为多少呢?

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person {
public:
	 void BuyTicket() { cout << "买票-半价" << endl; }

private:
	int _a;
	char _b;
};


int main()
{
	Student b;
	cout << sizeof(Student) << endl;
	return 0;
}

 答案:

 因为64位操作系统按8进行内存对齐。

如下,如果fun1,fun2是虚函数,fun3不是虚函数,那么fun3就不在vfptr里面。

虚函数实际上在代码段里面,只是把地址放在vfptr里面而已。

class Student : public Person {
public:
	
	virtual void fun1() { cout << "fun1()" << endl; }
	virtual void fun2(){ cout << "fun2()" << endl; }
	 void fun3(){ cout << "fun3()" << endl; }

};
int main()
{
	Student b;
	return 0;
}

多态是怎么来的

观察下面地址和代码,我们发现子类只是把父类的BuyTicket()函数重写了,func()函数没有重写,还是同一个func()。



class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
	virtual void fun(){}
};


class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
	
};


void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

调用BuyTick()函数的时候它也不知道调父类的BuyTick()函数还是子类的BuyTick()函数,但是它会去调vfptr,如果调父类就通过虚函数表去调父类的BuyTick(),如果调子类就对父类的vfptr切片,仍然通过vfptr调BuyTick()​​​​​​​,不过此时BuyTick()​​​​​​​已经是子类的BuyTick()了。

  • 0
    点赞
  • 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
发出的红包

打赏作者

孙鹏宇.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值