一、赋值兼容规则
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.youkuaiyun.com/snow_5288/article/details/70197366