学习c++多态的几个小例子

C++多态与模板示例
本文通过C++代码示例介绍了多态性的概念及其应用,包括虚函数、覆盖与隐藏的区别,以及如何利用模板实现静态多态。
//reference: http://blog.youkuaiyun.com/hackbuteer1/article/details/7475622

#include<iostream> 
#include<string>
#include <vector>

using namespace std;  

class Time
{
public:
	int hour;
	int minute;
	int second;

protected:
private:
};

Time t1={14,56,30}; 

class A  
{
	public:  
		void foo()  
		{
			printf("A foo()\n");  
		}

		virtual void fun()  
		{
			printf("A virtual fun()\n");  
		}
};

class B : public A  
{
	public:  
		void foo()  
		{
			printf("B foo()\n");  
		}

		void fun()  
		{
			printf("B virtual fun()\n");  
		}
};

class Base  
{
	public:  
		virtual void f(float x)  
		{
			cout<<"Base::f(float)"<< x <<endl; 
		}

		void g(float x)  
		{
			cout<<"Base::g(float)"<< x <<endl;  
		}

		void h(float x)  
		{  
			cout<<"Base::h(float)"<< x <<endl;  
		}  
};

class Derived : public Base  
{
public:
	//Derived::f(float)覆盖了Base::f(float)。
	virtual void f(float x)  
	{
		cout<<"Derived::f(float)"<< x <<endl;   //多态、覆盖
	}

	//Derived::g(int)隐藏了Base::g(float),而不是重载
	void g(int x)  
	{
		cout<<"Derived::g(int)"<< x <<endl;     //隐藏,不是重载
	}

	//Derived::h(float)隐藏了Base::h(float),而不是覆盖。
	void h(float x)  
	{
		cout<<"Derived::h(float)"<< x <<endl;   //隐藏 
	}
};

class Shape//形状
{
public:
	virtual void DrawSelf()//绘制自己
	{
		cout << "我是一个什么也绘不出的图形" << endl;
	}
};

class Polygo:public Shape//多边形
{
	public:
		virtual void DrawSelf()
		{
			cout << "连接各顶点" << endl;
		}
};

class Circ:public Shape//圆
{
public:
	virtual void DrawSelf()   //绘制自己
	{
		cout << "以圆心和半径为依据画弧" << endl;
	}
};

void OutputShape( Shape arg)//专门负责调用形状的绘制自己的函数
{
	arg.DrawSelf();
}

void OutputShape1( Shape& arg)//专门负责调用形状的绘制自己的函数

{
	arg.DrawSelf();
}

void displayShape(Shape *arg)
{
	arg->DrawSelf();
}

int my_add(int a, int b)
{
	return a + b;
}

int my_add(int a, std::string b)
{
	return a + atoi(b.c_str());
}

//宏多态, 带变量的宏可以实现一种初级形式的静态多态
#define ADD(A, B) ((A) + (B))

class Vehicle
{
public:
	virtual void run() const = 0;	//纯虚函数,该类为抽象类,不能实例化对象
};

// 派生于Vehicle的具体类Car
class Car: public Vehicle
{
public:
	virtual void run() const
	{
		std::cout << "run a car\n";
	}
};

class Airplane: public Vehicle
{
public:
	virtual void run() const
	{
		std::cout << "run a airplane\n";
	}

	void add_oil() const
	{
		std::cout << "add oil to airplane\n";
	}
}; 

class Airship: public Vehicle
{
public:
	virtual void run() const
	{
		std::cout << "run a airship\n";
	}

	void add_oil() const
	{
		std::cout << "add oil to airship\n";
	}
};

void run_vehicle(const Vehicle* vehicle)
{
	vehicle->run();            // 根据vehicle的具体类型调用对应的run()
}

// run异质vehicles集合
void run_vehicles(const std::vector<Vehicle*>&vehicles)
{
	for (int i = 0; i < vehicles.size(); i++)
	{
		vehicles[i]->run();
	}
}

// 为某种特定的aircrafts同质对象集合进行“空中加油”
template <typename Aircraft>
void add_oil_to_aircrafts_in_the_sky(const std::vector<Aircraft>& aircrafts)
{
	for (unsigned int i = 0; i < aircrafts.size(); i++)
	{
		aircrafts[i].add_oil();
	}
};

//静态多态
class SCar
{
	public:
		void run() const
		{
			std::cout << "run a SCar\n";
		}
};

class SAirplane
{
public:
	void run() const
	{
		std::cout << "run a SAirplane\n";
	}
};

// 通过引用而run任何vehicle
template <typename Vehicle>
void run_svehicle(const Vehicle& vehicle)
{
	vehicle.run();            // 根据vehicle的具体类型调用对应的run()
}

// run同质vehicles集合
template <typename Vehicle>
void run_svehicles(const std::vector<Vehicle>& vehicles)
{
	for (unsigned int i = 0; i < vehicles.size(); i++)
	{
		vehicles[i].run();
	}
}

int main()
{
	 //A a;  
	 //B b;  

	 //a.foo();
	 //a.fun();
	 //b.foo();
	 //b.fun();

	 //A *p = &a;  
	 //p->foo();
	 //p->fun();

	 //p = &b;  
	 //p->foo();
	 //p->fun();

	 //B *ptr = (B *)&a;  
	 //ptr->foo();  
	 //ptr->fun();
	 //cout<<endl;

	 //Derived d;  
	 //Base *pb = &d;  
	 //Derived *pd = &d;  

	 //pb->f(3.14f);   //Derived, 多态
	 //pd->f(3.14f);   //Derived

	 //pb->g(3.14f);   //Base
	 //pd->g(3.14f);	  //Derived

	 //pb->h(3.14f);   //Base
	 //pd->h(3.14f);	  //Derived

	Polygo shape1;
	Circ shape2;

	shape1.DrawSelf();
	shape2.DrawSelf();

	displayShape(&shape1);
	displayShape(&shape2);
	
	OutputShape1(shape1);
	OutputShape1(shape2);

	//向上转型中的子类型信息丢失
	OutputShape(shape1);
	OutputShape(shape2);

	int i = my_add(1, 2);                // 两个整数相加
	int s = my_add(1, "2");              // 一个整数和一个字符串相加
	std::cout << "i = " << i << "\n";
	std::cout << "s = " << s << "\n";

	int i1(1), i2(2);
	string s1("Hello, "), s2("world!");
	int is = ADD(i1, i2);
	string ss = ADD(s1, s2);
	cout<<"is = "<<is<<endl;
	cout<<"ss = "<<ss<<endl;
	cout<<ADD(3, 9.3434)<<endl;
	cout<<ADD(2, 3434.f)<<endl;
	cout<<ADD("sdf", 8)<<endl;
	cout<<ADD("sdf", '2')<<endl;
	cout<<ADD('A', 32)<<endl;
	cout<<ADD('A', '0')<<endl;

	Car car;
	Airplane airplane;
	run_vehicle(&car);         // 调用Car::run()
	run_vehicle(&airplane);    // 调用Airplane::run()

	std::vector<Vehicle*>v;
	v.push_back(&car);
	v.push_back(&airplane);
	run_vehicles(v);
	cout<<endl;

	Car car11, car12;
	Airplane airplane11, airplane12;
	Airship airship11, airship12;

	std::vector<Vehicle*> v1;                // 异质vehicles集合
	v1.push_back(&car11);
	v1.push_back(&airplane11);
	v1.push_back(&airship11);
	run_vehicles(v1);                        // run不同种类的vehicles

	std::vector<Airplane> vp;  
	vp.push_back(airplane11);
	vp.push_back(airplane12);
	add_oil_to_aircrafts_in_the_sky(vp);    // 为airplanes进行“空中加油”

	std::vector<Airship> vs1;
	vs1.push_back(airship11);
	vs1.push_back(airship12);
	add_oil_to_aircrafts_in_the_sky(vs1);    // 为airships进行“空中加油”
	cout<<endl;

	SCar scar;
	SAirplane sairplane;
	run_svehicle(scar);         // 调用Car::run()
	run_svehicle(sairplane);    // 调用Airplane::run()
	cout<<endl;

	SCar car1, car2, car3;
	SAirplane airplane1, airplane2,  airplane3;
	std::vector<SCar> vc; 
	vc.push_back(car1);
	vc.push_back(car2);
	vc.push_back(car3);
	//vc.push_back(airplane1);
	run_svehicles(vc);

	std::vector<SAirplane> vs; 
	vs.push_back(airplane1);
	vs.push_back(airplane2);
	vs.push_back(airplane3);
	run_svehicles(vs);

	return 0;
}

### C++ 中的多态机制 C++ 中的多态机制主要依赖于虚函数(virtual function)和虚函数表(vtable)来实现。多态允许基类指针或引用在运行时动态地绑定到派生类对象,并调用该对象的实际方法。这种行为被称为**运行时多态****动态绑定**。 #### 虚函数与虚函数表 当在类中声明一个函数为 `virtual` 时,该函数就成为虚函数。编译器会为每个包含虚函数的类生成一个虚函数表(vtable),这是一个函数指针数组,其中每个指针指向类中虚函数的实际实现。每个对象在内存中会维护一个指向其类虚函数表的指针(vptr)[^1]。 例如: ```cpp class Animal { public: virtual void Speak() { std::cout << "animal is speak" << std::endl; } }; class Cat : public Animal { public: void Speak() override { std::cout << "cat is speak" << std::endl; } }; ``` 在上述代码中,`Animal` 和 `Cat` 都有各自的虚函数表。当 `Cat` 对象被创建时,它的虚函数表指针将指向 `Cat` 的虚函数表,而不是 `Animal` 的虚函数表。这样,即使通过 `Animal` 类型的指针或引用访问对象,程序也能调用到 `Cat` 的 `Speak()` 函数[^5]。 #### 多态的使用方法 要实现多态,必须满足以下几个条件: 1. **基类中的虚函数必须被派生类重写(override)**。 2. **通过基类的指针或引用访问派生类对象**。 3. **虚函数的调用必须发生在运行时**。 示例: ```cpp void DoSpeak(Animal& animal) { animal.Speak(); // 根据实际对象类型调用 Speak() } int main() { Animal animal; Cat cat; DoSpeak(animal); // 输出 "animal is speak" DoSpeak(cat); // 输出 "cat is speak" } ``` 在这个例子中,`DoSpeak` 接收一个 `Animal` 类型的引用,但传入的是 `Cat` 对象。由于 `Speak()` 是虚函数,运行时会根据对象的真实类型调用相应的实现。 #### 虚函数表的存储机制 每个类的虚函数表是唯一的,所有该类的对象共享同一个虚函数表。对象的虚函数表指针通常位于对象的起始位置。当对象被构造时,编译器会自动设置虚函数表指针,使其指向该类的虚函数表。在继承关系中,派生类会继承基类的虚函数表,并在必要时覆盖或扩展虚函数表中的条目[^2]。 #### 使用 `std::variant` 与 `std::visit` 实现多态 除了传统的虚函数机制,C++17 引入的 `std::variant` 和 `std::visit` 提供了一种非面向对象的多态实现方式。`std::variant` 可以持有多个类型的值,而 `std::visit` 可以根据当前持有的类型执行相应的操作。 示例: ```cpp struct CallPrint { void operator()(const Base& b) { b.Print(); } void operator()(const Derived& d) { d.Print(); } }; int main() { std::variant<Base, Derived> v = Derived(); std::visit(CallPrint{}, v); // 调用 Derived::Print() v = Base(); std::visit(CallPrint{}, v); // 调用 Base::Print() } ``` 这种方式避免了虚函数表和继承的复杂性,适用于需要在多个类型之间进行选择性操作的场景[^3]。 #### 多态的注意事项 - 虚函数的性能开销:由于虚函数需要通过虚函数表间接调用,因此相比普通函数调用,它有一定的性能开销。 - 虚析构函数:如果基类可能被继承,建议将析构函数声明为虚函数,以确保派生类的析构函数能够被正确调用。 - 避免在构造函数中调用虚函数:在构造函数执行期间,对象的虚函数表指针尚未完全初始化,因此调用虚函数可能会导致未定义行为。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值