继承体系下派生类对象模型(单继承、多继承、菱形继承、虚拟继承、菱形虚拟继承)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wuxinrenping/article/details/80329959

一、赋值兼容规则

   1.子类对象可以赋值给父类对象。反之则不成立。 

    这是因为你可以将子类看做父类的一个对象,在通俗点,子类中拥有父类的数据,可以将其赋给父类,可是父类中并不包含子类中拥有的自己的独立数据,所以赋值不了给子类。

  2.父类的指针/引用可以指向子类对象

    这是因为父类想找一块和自己相似的地址空间,而子类中存在父类中的所有,这是可以指向。而子类想要一块和自己类似的空间,父类中不存在子类的独立数据,所以是没有的。所以子类是没有办法指向父类的。

 懂得了赋值兼容规则,我们了解一下不同继承体系下的对象的模型

二、单继承

class Base
{
public:
Base()
{
cout << "Base::Base()" << endl;
}
int _b;
};


class Derived :public Base
{
public:
Derived()
{
cout << "Derived::Derived()" << endl;
}

int _d;
};

int main()
{
Derived d;
d._b = 1;
d._d = 2;
return 0;

}

通过内存窗口的查看,可以看出基类的数据在子类数据之上。子类的大小为8

   在这里为大家扩展一个知识点,如果继承时父类和子类存在了同名的数据,那么会造成问题吗???

class D :public A
{
public:
int _data;
};
int main()
{
C c;
d._data = 1;
c._c = 2;

return 0;

}

   如上述代码,发现编译可以通过,那么子类对象的结构和单继承的对象结构一样吗,我们调一下内存窗口来看一下。

  我们发现,其对象结构和单继承下的对象结构是相同的,只是如果不写上作用域,则会就近调用子类中的数据,要想修改父类的数据,可以加上作用域。

d.A::_data = 2;

三、多继承

class Base1
{
public:
Base1()
{
cout << "Base1::Base1()" << endl;
}


int _b1;
};

class Base2
{
public:
Base2()
{
cout << "Base2::Base2()" << endl;
}


int _b2;
};

class Derived :public Base1,public Base2
{
public:
Derived()
{
cout << "Derived::Derived()" << endl;
}

int _d;
};

int main()
{
Derived d;
d._b1 = 1;
d._b2 = 2;
d._d = 3;
return 0;
}

子类的大小为12

  三、菱形继承

class C
{
public:
C()
{
cout << "C::C()" << endl;
}

int _c;
};

class Base1 :public C
{
public:
Base1()
{
cout << "Base1::Base1()" << endl;
}

int _b1;
};

class Base2 :public C
{
public:
Base2()
{
cout << "Base2::Base2()" << endl;
}

int _b2;
};

class Derived :public Base1,public Base2
{
public:
Derived()
{
cout << "Derived::Derived()" << endl;
}

int _d;
};

int main()
{
Derived d;
int size = sizeof(d);
d._c = 1;
d._b1 = 2;
d._b2 = 3;
d._d = 4;
return 0;
}

  

编译产生错误。画图解释。

上述即是菱形继承的二义性问题,这时有两种解决办法

1.在变量或函数前加上作用域

int main()
{
Derived d;
int size = sizeof(d);
d.Base1::_c = 0;
d.Base2::_c = 1;
d._b1 = 2;
d._b2 = 3;
d._d = 4;
return 0;

}

2.为了只让_c出现一份,这时就涉及到了菱形虚拟继承。在此之前先讲一下虚拟继承

四、虚拟继承

#if 1
class C
{
public:
C()
{
cout << "C::C()" << endl;
}


int _c;
};

class D :virtual public C
{
public:
D()
{
cout << "D::D()" << endl;
}
int _d;
};
#endif

int main()
{
D d;
d._c = 1;
d._d = 2;
return 0;
}

此时地址放的是什么呢???

    

此时存放的是基类于当前对象起始位置的偏移量。大小为12

四、菱形虚拟继承

#if 1
class C
{
public:
C()
{
cout << "C::C()" << endl;
}


int _c;  
};

class Base1 : virtual public C
{
public:
Base1()
{
cout << "Base1::Base1()" << endl;
}


int _b1;
};

class Base2 :virtual public C
{
public:
Base2()
{
cout << "Base2::Base2()" << endl;
}


int _b2;
};

class Derived :public Base1,public Base2
{
public:
Derived()
{
cout << "Derived::Derived()" << endl;
}

int _d;
};

int main()
{
Derived d;
int size = sizeof(d);
d._c = 1;
d._b1 = 2;
d._b2 = 3;
d._d = 4;
return 0;
}

#endif

 此时Base1与基类偏移20个字节,Base2与基类偏移12个字节

这个D类的大小为24.

五、静态数据可以继承吗?

class B
{
public:
B()
{
cout << "B::B()" << endl;
}
static int _b;
};


class C :public B
{
public:
C()
{
cout << "C::C()" << endl;
}
int _c;
};


int B::_b = 1;
//int C::_b = 2;


int main()
{
C c;
int size = sizeof(c);

c._b = 2;

        c._c = 3;
cout << c._b << endl;

}

此时注意三点,静态成员变量继承时,只能用基类赋值,用子类赋值时会出现警告。

                        静态成员变量在静态区,内存窗口显示不出来,只能显示输出。

                         静态成员变量不占空间大小,所以子类大小为4

六、友元函数能继承吗?

   因为友元函数并不是类中的成员,所以自然是不能被继承的。举例????

  但是在派生类中,因为基类的引用时可以指向派生类的,所以派生类在特殊情况下是可以调用友元函数的。

class B
{
public:
B()
{
cout << "B::B()" << endl;
}
friend void print(const B& b);


int _b;
};

void print(B & b)
{
cout << b._b<< endl;
}

class C :public B
{
public:
C()
{
cout << "C::C()" << endl;
}


int _c;

};

int main()
{
C c;
int size = sizeof(c);
c._b = 2;
c._c = 3;
print(c);
}

七、设计一个类,该类不能被继承

https://www.cnblogs.com/TenosDoIt/p/3641943.html

八、用C语言模拟实现继承

https://blog.csdn.net/snow_5288/article/details/70197366

猜你喜欢

转载自blog.csdn.net/wuxinrenping/article/details/80329959