之前我们写过c++之虚函数表及多态,讨论了存在虚函数时的虚函数表变化及继承关系。今天讨论一下类的对象的存储方式。其中参考C++类对象的内存结构。
一、类对象内存结构
因为静态成员属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间
在c++基类中,如果存在虚函数,那么它就会有一个指向虚函数表的指针vptr(vptr具体在什么位置,与编译器有关,大多数都在开始处),之后是类的成员的内存数据。而对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量),之后才是自己的成员的内存数据。至于虚函数表的变化,可以参考c++之虚函数表及多态。
例如存在一个虚函数继承:
class Base
{
public:
virtual void f() { cout << “Base::f” << endl; }
virtual void g() { cout << “Base::g” << endl; }
virtual void h() { cout << “Base::h” << endl; }
int base;
protected:
private:
};
class Child2 : public Base
{
public:
virtual void f() { cout << “Child2::f” << endl; }
virtual void g1() { cout << “Child2::g1” << endl; }
virtual void h1() { cout << “Child2::h1” << endl; }
int child2;
protected:
private:
};
二、对象不能实现多态
#include<iostream>
#include<string>
using namespace std;
class base
{
public:
virtual ~base() { cout << "析构基类" << endl; };
virtual string GetName() { return "base"; }
void GetA() { cout << "Base::GetA" << endl; };
int a=1;
};
class derived : public base
{
public:
virtual ~derived() { cout << "析构子类" << endl; };
virtual string GetName() { return "derived"; }
void GetB() { cout << "Derived::GetB" << endl; };
int a = 100;
int b = 200;
};
int main()
{
base B1;
base *B2 = new base();
derived D1;
//子类对象可以给父类对象赋值,而父类对象不能给子类对象赋值
B1 = D1;
B1.GetA();
cout << B1.GetName() << endl;
//即使赋值操作,基类的a并没有被子类的a所覆盖。
cout << "a = " << B1.a << endl;
//delete[] B1;
B2 = new derived();
B2->GetA();
//只有这一个体现了多态(虚函数继承),指针和引用才能实现多态。
cout<<B2->GetName()<<endl;
cout << "a = " << B2->a << endl;
delete B2;
system("pause");
}
结论:无论赋值操作还是赋值构造时,一个类对象里面的vptr永远不会变,永远都会指向 所属类 的虚函数表。而当使用指针或者引用时,vptr会指向 继承类 的虚函数表。