关于C++中 虚函数 多态 和 访问限定符的思考

本文深入探讨了C++中虚函数表的结构和位置,以及静态编译和动态编译的机制。同时,文章还展开讨论了纯虚函数是否可以被声明为private的问题,涉及基类中private和virtual private函数的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2016-04-12 

1、C++中的静态编译和动态编译分别基于什么机制实现?

首先,静态编译和动态编译都属于C++中的多态,即“一种接口,多种实现”。
静态编译(/联编) —— 也称为 静态多态    如:函数的重载和运算符的重载
动态编译(/联编 ) ——也称为运行时多态  如:虚函数

多态规则:
     通过基类指针或引用调用成员函数时
          如果函数是非虚函数,采用静态编译(即:编译时绑定)。
          如果函数是虚函数,采用动态编译(即:运行时绑定)。

 --- 动态联编
          指的是被调函数的入口地址是在运行时才确定的。
      ** C++实现动态联编是基于 虚指针(vptr) 和 虚函数表(vtable)
     具体为:
          a. 为每一个包含虚函数的类建立一个虚函数表,表中存放的是各虚函数在内存中的入口地址。
          b. 包含虚函数的类的每个对象都有一个指向虚函数表的 虚指针。
          c. 调用虚函数时,利用虚指针找到虚函数表,进而确定虚函数的入口地址,最后调用之。

     注: 一个包含虚函数的类只有一个虚函数表。

     ** 虚函数表的结构

          a. 一个类中虚函数的来源包括: 继承自父类(需要改写函数) + 当前类中新声明的虚函数
          b. 为所有虚函数排序:继承的虚函数在前,新声明的虚函数按照声明顺序排列在后。

     ** 虚函数表的位置

          虚函数表的存放位置 在应用程序的常量区

2、进阶讨论 纯虚函数是否可以声明为private?

          可以
          但与声明为public的区别在于:虚函数不能在类外部调用,只能在内部调用 or 通过类所提供的public接口调用。

     ** 关于基类中函数声明为 private 和 virtual private 的一点讨论    

                --- 首先,声明为 private,表示基类不希望外部看到这个函数,子类当然也就无法覆盖或重载这个函数,
               看起来这个函数的实现是无法修改的。但是如果我们在基类中定义一个公共接口,该接口对private函
               数进行了调用,子类可以对公共接口进行修改达到"修改"private函数的假象。
                --- 其次,声明为 virtual private,那么既希望子类对其重写实现多态,又不希望子类看到这个函数。看似
               自相矛盾。这里实际的意义在于: 子类可以修改函数的实现,也可以继承基类中默认的实现。
               --- 最后,声明为virtual protected,其意义在于 这是必须被子类修改的实现
               P.S: 最好不要把虚函数声明为public,而是用protected来替换。
      结论:多态与虚函数前的访问限定符无关,限定符仅仅限制外部对类成员的访问权限。

恩 说了这么多 给个例子。
#include <iostream>
#include <string>

using namespace std;

// 父类/基类
class CMyBase
{
	// 多态的实现只与是否是虚函数有关 与访问限定符public private protected无关
private:
//public:
	// 可以看到 输出结果只与该函数是否为virtual有关 与访问限定符无关
	//virtual string getObj() const
	string getObj() const
	{
		return string("CMyBase");
	}

protected:
	virtual void DoSth() = 0;	// 纯虚函数
public:
	// 外部访问虚函数的接口(如果getObj()函数声明为private的话)
	void DoSthAgain()
	{
		cout << "This is "<< getObj() <<endl;
		DoSth();
	}
	
	// 虚析构函数
	virtual ~CMyBase(){}
};

// 子类/派生类
class CMyDrived : public CMyBase
{
private:
	string getObj() const
	{
		return string("CMyDrived");
	}
protected:
	void DoSth()
	{
		cout<<"This is CMyDrived"<<endl;
	}
};

int main(void)
{
	CMyBase *p = new CMyDrived();
	p->DoSthAgain();

	return 0;
}

getObj()声明为virtual时,输出第一行才是CMyDerived的信息,这时是动态联编。
否则无论访问限定符是什么(public private 抑或 protected),输出第一行都是CMyBase的信息。
分析一下:
函数并没有声明为virtual,那么就只是一个普通函数对不对,普通的成员函数是在编译期就决定的,属于静态联编。
因此根本就不关继承不继承什么事,对不对。这仅仅只是类内部普通成员函数的调用而已。
所以,输出信息肯定只是基类的输出,与子类无关。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值