关于C++中的多态

多态——在我写这篇文章之前,我一直认为多态指的是“运行时多态”,就是在基类中借助virtual关键字实现的。

好,看到这里,忘记上面那句话。

1. 多态是如何实现绑定的

多态的绑定可以分为运行时多态和编译时多态。

编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数决定实现何种操作。(有些人写的是根据参数列表和返回值,但返回值在C++中式可以忽略的,因为其实返回值并不影响编译器的决定)

运行时多态是由虚函数实现的,是指直到系统运行时,才根据实际对象决定选择何种操作。

2.多态的实现

C++只要在基类中在函数前加上virtual关键字即可声明虚函数,子类中不需要virtual关键字,只要函数的返回类型与函数名称及参数列表与基类中一致,将自动成为虚函数。

#include<iostream>
using namespace std;

class A
{
public:
	void foo()
	{
		printf("1\n");
	}
	virtual void fun()
	{
		printf("2\n");
	}
};
class B : public A
{
public:
	void foo()
	{
		printf("3\n");
	}
	void fun()
	{
		printf("4\n");
	}
};
int main(void)
{
	A a;
	B b;
	A *p = &a;
	p->foo();
	p->fun();
	p = &b;
	p->foo();
	p->fun();
	return 0;
}
注意p=&b时,A类指针p指向的是B类对象,你觉得他会输出什么?自己试试看就知道了

关于虚函数的问题我自己今天突发奇想说虚函数在子类中是不是跟函数重载一样不看返回类型?试了下以下代码:

#include <iostream>
using namespace std;


class ClxBase
{
public:
    ClxBase() {};
    virtual ~ClxBase() { cout << "Do something in class ClxBase!" << endl; };

    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase
{
public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };

    string DoSomething() { cout << "Do something in class ClxDerived!" << endl; return "Do something in class ClxDerived!";};
};

int main(){
    ClxBase *pTest = new ClxDerived;
    pTest->DoSomething();
    delete pTest;
}
结果连编译都没通过……,codeblocks直接提示conflicting return type specified for `virtual std::string ClxDerived::DoSomething()'哎呀笨死了……子类虽然没有声明virtual但是它也继承了基类的DoSomething啊,我这不是重定义么……


多态的实现原理是什么?搞明白再回来写过

### C++多态的实现机制详解 在C++中,多态是通过虚函数表(vtable)和虚函数表指针(vptr)来实现的。每个具有虚函数的类都有一个对应的虚函数表,它是一个数组,其中包含指向该类所有虚函数的指针。当一个对象被创建时,其内部会有一个指向该类虚函数表的指针,这个指针通常位于对象内存布局的最开始位置[^1]。 对于继承体系中的派生类,如果它重写了基类的虚函数,则会在自己的虚函数表中替换掉相应条目为新的虚函数地址;若新增了虚函数,则会在虚函数表后面追加这些函数的指针。这样,即使使用基类类型的指针或引用访问派生类对象,也可以根据实际对象类型调用正确的虚函数版本,从而实现了运行时多态性[^2]。 考虑以下示例代码: ```cpp #include <iostream> using namespace std; class Base { public: virtual void show() { cout << "Base show" << endl; } virtual ~Base() {} // 确保有虚析构函数以正确释放资源 }; class Derived : public Base { public: void show() override { cout << "Derived show" << endl; } }; int main() { Derived d; Base* b = &d; b->show(); // 输出 "Derived show" return 0; } ``` 在这个例子中,`Base`类定义了一个虚函数`show()`,而`Derived`类重写了这个函数。主函数中声明了一个`Base`类型的指针`b`,并让它指向了一个`Derived`对象`d`。尽管`b`是指向`Base`的指针,但调用`b->show()`时却执行了`Derived`版本的`show()`方法,这正是由于虚函数机制的作用[^1]。 值得注意的是,静态绑定与动态绑定的区别在于:静态绑定发生在编译时期,适用于非虚函数调用;而动态绑定则是在程序运行期间通过虚函数表完成的。只有当通过指针或引用调用虚函数时才会触发动态绑定,进而表现出多态行为[^1]。 此外,在构造和析构过程中,虚函数的行为有所不同。如果在基类构造函数或者析构函数内部调用了虚函数,那么此时不会发生多态行为,因为当前正在构造或销毁的对象尚未完全形成或已经被部分销毁,因此只能调用到当前阶段所属类的虚函数版本[^2]。 最后,为了确保能够正确地进行多态操作,应当始终为含有虚函数的类提供虚析构函数。这是因为如果不这样做,当删除一个指向派生类对象的基类指针时,可能无法正确地调用派生类的析构函数,导致未定义行为。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值