class Base
{
virutal hi() { }
};
class Child : public Base
{
public:
Child():m_c(10)
{
}
int m_c;
};
int main()
{
Base* ptr=new Child() ;
cout<<ptr->m_c; //通过基类访问子类,没有啥问题呀?
return 0;
}
编译出错 ,修复如下
//method1
cout<<"cast= "<<((Child*)ptr)->m_c<<endl;
//method2
if( Child* pc=dynamic_cast<Child*>(ptr))
{
cout<<"dynamic_cast="<<pc->m_c<<endl;
}
深度探索C++对象模型 1.3 章节 https://github.com/wangcy6/weekly/blob/master/reading-notes/object-model/1.object-lessons.md
Child c;//
Base *pz = &c; //
Child *pb = &c;
Each addresses the same first byte of the Child object.
The difference is that the address span of pb
encompasses the entire Bear object,
while the span of pz encompasses only the ZooAnimal subobject of
Bear.
pz cannot directly access any members other than those present within the ZooAnimal subobject, except
through the virtual mechanism:
pz pb 虽然指向 地址 同一个地址,没有任何差别,但是对地址解析 需要根据定义类型来来判断 Child 和 Base 大小不一样
Bear b;
ZooAnimal za = b;
// ZooAnimal::rotate() invoked
za.rotate();
So, then, why is it that, given the instance of rotate() invoked is the ZooAnimal instance and not that of Bear? Moreover, if memberwise initialization copies the values of one object to another,
ZooAnimal za = b;
why is za's vptr not addressing Bear's virtual table?
The answer to the second question is that the compiler intercedes in the initialization and assignment of one
class object with another.
The compiler must ensure that if an object contains one or more vptrs, those vptr
values are not initialized or changed by the source object .
译成中文就是,编译器必须要确保如果一个对象有一个或多个vptr,这些vptr不是由原对象来初始化或改变的。 也就是说:当使用赋值的方式或拷贝构造的方式创建一个对象时,这个对象的vptr与源对象无关。
回答1 The answer to the first question is that za is not (and can never be) a Bear; it is (and can never be anything but) a ZooAnimal. Polymorphism, the potential to be of more than one type, is not physically possible in directly accessed objects
在《深度探索C++对象模型》的4.2节能够找到完美答案,具体摘抄如下: “表格中的virtual functions地址是如何被建构起来的?在C++中,virtual functions(可经由其class object被调用)可以在编译时期获知。此外,这一组地址是固定不变的,执行期不可能新增或替换之。由于程序执行时,表格的大小和内容都不会改变,所以其建构和存取皆可以由编译器完全掌控,不需要执行期的任何介入
注意:这种说法是错误的(编译器太懒了)
正确的说法: 惟有默认构造函数”被需要“的时候编译器才会合成默认构造函数 默认构造函数是指有用的默认构造函数,其英文名字叫nontrivial default constructor。 对于以下四种情况,编译器会自动生成默认构造函数
有基类的构造函数,有成员的构造函数,还有自己构造函数,哪有优先执行(混乱)? 派生类构造函数执行顺序:
正确的说法: 对于默认构造函数与复制构造函数,都需要类满足一定的条件时编译器才会帮你合成。那么需要满足些什么条件呢?这条件就是:类不展现bitwise copy 语意的时候 那么在什么情况下一个类才会不展现出Bitwise copy 语意呢? 一个类对于默认的拷贝赋值操作, 在以下情况不会表现出bitwise拷贝语意:
* 当类内带一个成员对象, 而其类有一个拷贝赋值操作时
* 当一个类的基类有一个拷贝赋值操作时
* 当一个类声明了任何虚函数(我们一定不能拷贝右端类对象的vptr地址, 因
为它可能是一个继承类对象)
* 当类继承自一个虚基类(不论此基类有没有拷贝操作)时
https://github.com/wangcy6/weekly/blob/master/reading-notes/object-model/5.copy-construction-semantics.md
[2 vptr初始化语义 The Semantics of the vptr Initialization 第5.2章节]
正确理解: 在父类构造函数之后,子类构造函数之前(因为子类构造函数可能执virtual函数) After invocation of the base class constructors but before execution of user-provided code or the expansion of members initialized within the member initialization list
The Semantics of the vptr Initialization
The Semantics of the vptr Initialization
构造函数的执行算法通常如下:
编程规范 不要在构造函数中调用虚函数
程序员的自我修养6.4.2节,关于ELF各个section的解释见 在gcc编译器的实现中虚函数表vtable存放在可执行文件的只读数据段.rodata中
虚表属于一个类,里面存储自己的不同对象
构造函数的执行算法