深度探索C++ 对象模型(阅读笔记)(原创)
Inside The C++ Object Model
--构造函数语意学
由 王宇 原创并发布 :
让我肃然起敬并崇拜的牛人:
Bjarne Stroustrup - C++之父 (www.stroustrup.com)
Stanley B. Lippman - 本书作者、C++代言人、C++编译器的设计者之一
侯捷 - 本书译者 (jjhou.boolan.com)
第二章 构造函数语言学
英文术语:
implicit :暗中、隐含的(通常意指并非在程序源代码中出现的)
explicit :明确(通常意指程序源代码中所出现的)
trival :没有用的
nontrival :有用的
memberwise:对每一个member施以...
bitwise :对每一个bit施以...
semantics :语意
2.1 Default Constructor 的建构操作
“default constructors... 在需要的时候被编译器产生出来”。关键字是“在需要的时候”
“带有 Default Constructor”的Member Class Object:
如果一个class 没有任何constructor,但它内含一个member object, 而后者有default constructor, 那么这个class的implicit default constructor就是nontrivial,编译器需要为此class合成出一个default constructor。不过这个合成操作只有在constructor真正需要被调用时才会发生
例:
class Foo { public: Foo(); //构造函数 ... }; class Bar { public: Foo foo; //是一个member object, 而其class Foo 拥有default constructor. char *str; }; void foo_bar() { Bar bar //合成constructor }
注意:被合成的default constructor只满足编译器的需要,而不是程序的需要。
“带有Default Constructor”的Base Class
如果一个没有任何constructor的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor(根据它们的声明次序)。对一个后派生的class而言,这个合成的constructor和一个“被明确提供的default constructor”没有什么差异。
“带有一个Virtual Function”的Class
另有两种情况,也需要合成出default constructor
(1)class声明(或继承)一个 virtual function
(2)class派生自一个继承串链,其中有一个或更多的virtual base classes
总结:
C++ Stardand 把那些合成物称为:implicit nontrivial default constructor。被合成出来的constructor只能满足编译器的需要,而非程序的需要。它之所以能够完成任务,是借着“调用member object或base class的default constructor”或是“为每一个object初始化其virtual function机制或virtual base class机制”而完成。至于没有存在那四种情况而又没有声明任何constructor的class,我们说它们拥有的是implicit trivial default constructor,它们实际上并不会被合成出来。
在合成default constructor中,只有base class subobject和member class objects会被初始化。所有其它的nonstatic data member,如整数、整数指针、整数数组等等都不会被初始化。这些初始化操作对程序而言或许有需要,但对编译器则并非必要。
C++新手一般有两个常见的误解:
(1)任何class如果没有定义default constructor,就会被合成出一个来。
(2)编译器合成出来的default constructor会明确设定“class 内每一个data member的默认值”
结论:无论是自定义或合成出来的构造函数,均需要自己初始化数据成员(除了member class objects)。
2.2 Copy Constructor建构操作
有三种情况,会以一个object的内容作为另一个class object的初值:
class X { ... }; X x; X xx = x; // 情况1,赋值对象 extern void foo( X x); void bar() { X xx; foo( xx ); // 情况2,作为参数 } X foo_bar() { X xx; return xx; // 情况3,作为返回值 }
Default Memberwise Initalization
如果class 没有提供一个explicit copy constructor又当如何?当class object 以 “相同class的另一个object”作为初值时,其内不是以所谓的default memberwise initalization手法完成的,也就是把每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object身上。不过它不会拷贝其中的member class object, 而是以递归的方式实行memberwise initalization.
例子:
class String { public: //..没有explicit copy constructor private: char *str; int len; }; class Word { public: //..没有explicit copy constructor private: int _occurs; String _word; //String object成为class word的一个member. 此处以递归的方式实行memberwise initalization. // Word 是否合成 copy constructor 取决于 bitwise copy semantics. //此例子不合成copy constructor 编译器会自动复制每一个数据成员 };
指出一个错误概念:“如果一个class未定义copy constructor,编译器就自动为它产生出一个”这句话不对
正确的概念:Default constructor 和 copy constructor在必要的时候才由编译器产生出来。“必要”意指当class不展现bitwise copy semantics时。
Bitwise Copy Semantics(位逐次拷贝)
上例展示了Bitwise copy Semantics.
有一点很值得注意:在被合成出来的copy constructor中,如整数、指针、数组等等的nonclass memebers也都会被复制,正如我们所期待的一样。
不要Bitwise Copy Semantics
有四种情况不展示Bitwise Copy Semantics, 不展示的时候需要编译器合成copy constructor:
(1)当class内含一个member object而后者的class声明有一个copy constructor时
(2)当class继承自一个base class 而后者存在有一个copy constructor时
(3)当class声明了一个或多个virtual functions时
(4)当class派生自一个继承串链,其中有一个或多个virtual base classes时
结论:如果是自定义复制构造函数时,需要自己把每一个数据成员复制;如果是没有自定义复制构造函数,无论是合成或非合成,编译器都会自动复制每一个数据成员。复制构造函数的用途是:如果构造函数中存在动态内存分配,则必须定义复制构造函数,否则会出现“指针悬挂问题”。
class A { private: int *p; public: A() { p = new int(3); } };
在这种情况下,复制对象,会造成两个对象的成员指向同一地址。
重新设定Virtual Table的指针
例子:
class ZooAninal { public: ZooAnimal(); virtual ~ZooAnimal(); virtual void animate(); virtual void draw(); }; class Bear : public ZooAnimal() { public: Bear(); void animate(); void draw(); virtual void dance(); };
Bear yogi;
Bear winnie = yogi;
把yogi 的vptr值拷贝给winnie的vptr是安全的
ZooAnimal franny = yogi; // 这会发生切割行为
合成出来的ZooAinmal copy constructor会明确设定object的vptr指向ZooAnimal class的virtual table,而不是直接从右手边的class object中将其vptr现值拷贝过来。
处理Virtual Base Class Subobject
2.3 程序转化语意学(Program Transformation Semantics)
明确的初始化操作(Explicit initialization)
X x0; void foo_bar() { X x1(x0); //定义了x1 X x2 = x0; //定义了x2 X x3 = x(x0); //定义了x3 } //可能的程序转换 // C++ 伪码 void foo_bar() { X x1; //定义被重写 X x2; //定义被重写 X x3; //定义被重写 //编译器安插X copy construction的调用操作 x1.X::X( x0 ); x2.X::X( x0 ); x3.X::X( x0 ); }
总结:程序转化有两个阶段:1、重写每一个定义 2、class的copy constructor调用操作会被安插进去
参数的初始化(Argument Initialization)
把一个class object当做参数传递给一个函数,相当于:X xx = arg;
void foo( X x0 ); X xx; foo(xx); //C++ 伪码 X __temp0; //编译器对copy constructor的调用 __temp0.X::X(xx); //重新改写函数调用操作,以便使用上述的暂时对象 foo(__temp0);
总结:编译器采用暂时性object策略,并调用copy constructor将它初始化,然后将该暂时性object交给函数。
返回值的初始化(Return Value Initialization)
X bar() { X xx; //处理xx ... return xx; } //函数转换 //C++伪码 void bar( X& __result) // 加上了一个额外参数 { X xx; //编译器所产生的default constructor调用操作 xx.X::X(); //处理xx ... //编译器所产生的copy constructor调用操作 __result.X::XX(xx); } /////////////////// X xx = bar(); //被转换为: X xx; bar( xx ); /////////////////// bar().memfunc(); //被转换为: X __temp0; (bar( __temp0 ), __temp0).memfunc(); //////////////////// x( *pf )(); pf = bar; //被转换为: void ( *pf )( X& ); pf = bar;
Copy Constructor:要还是不要?
正常情况下不需要,没有任何理由要你提供一个copy constructor函数实体,因为编译器自动为你实施了最好的行为。除非需要解决类似“指针悬挂”等问题时,需要Copy Constructor
2.4 成员们的初始化队伍
何时使用initialization list才有意义:
1、当初始化一个reference member时
2、当初始化一个const member时
例子:
class Shape { const int m_size; //const 常量 int & m_ref; // reference member float m_width; float m_height; public: Shape(int s,float w,float h):m_size(s),m_ref(s) //只能在这初始化 { //m_size =s; //在初始化将出错 m_width = w; m_height = h; } };
3、当调用一个base class的constructor,而它拥有一组参数时
例子:
class A { A(int x); // A 的构造函数 }; class B : public A { B(int x, int y); // B 的构造函数 }; B::B(int x, int y): A(x) // 必须在初始化表里调用基类 A 的构造函数 { … }
4、当调用一个member class的constructor,而它拥有一组参数时
例子:
class A { A(int x); // A 的构造函数 }; class B { A m_a; B(int x, int y); // B 的构造函数 }; B::B(int x, int y): m_a(x) // 在初始化表里调用基类 A 的构造函数 { … m_a = A(x); //赋值,并非初始化m_a }
initialization list的真正操作是什么:
例子:
class Word { String _name; int _cnt; public: Word() { _name = 0; // 可以编译功过并运行,但是效率低下 _cnt = 0; // 是否使用initialization list 都是相同的 } }; // C++ 伪码 Word::Word() { //调用String的 default constructor _name.String::String(); //产生暂时性对象 String temp = String(0); // "memberwise"地拷贝_name _name.String::operator=( temp ); //摧毁暂时性对象 temp.String::~String(); _cnt = 0; }
在这里,Word constructor会产生一个暂时性的String object,然后将它初始化,再以一个assignment运算符将暂时性object指定给_name,然后再摧毁那个暂时性object
//较佳的方式 Word::Word(): _name( 0 ) { _cnt = 0; } // C++ 伪码 Word::Word() { //调用String(int) constructor _name.String::String( 0 ); _cnt = 0; //这里是:是否使用initialization list 都是相同的原因 }
initialization list的顺序:
(1) list中的项目次序是由class中的members声明次序决定,不是由initialization list中的排序次序决定
例子:
class X { int i; int j; public: X(int val) : j( val ), i( j ) //注意这里有一个陷阱,i值为一个不可预知未初始化的值。原因是按照定义的顺序,i先被初始化,这个时候j还没有被初始化。 };
(2) initialization list的项目被放在explicit user code之前
例子:
class X { int i; int j; public: X(int val) : j( val ) { i = j; //是正确的,原因是initialization list的项目被放在explicit user code之前,及这里j已经在构造函数之前初始化了。 } };
(3) 调用一个member function 以设定一个member的初值
例子:
class X { int i; int xfoo( int val) { return val; } public: X(int val) : i ( xfoo(val) ) //这里是正确的 { } }; // C++ 伪码 X::X() { i = this->xfoo( val ); }
相关推荐
在深入探讨《深度探索C++对象模型》这一主题之前,我们首先需要理解C++对象模型的基本概念及其在程序设计中的重要性。C++作为一种广泛使用的编程语言,其强大的功能和灵活性使得它成为许多领域的首选语言之一。对于...
这是我见到相对比较清晰的版本,且有完整的书签
《深度探索C++对象模型》是一本专注于C++编程语言底层机制的专业书籍,它深入剖析了C++的对象模型,帮助读者理解C++是如何在内存中表示对象、如何实现继承、多态等特性。这本书的内容涵盖了C++的核心概念,包括类、...
《深度探索C++对象模型》是一本专门为C++程序员量身打造的专业书籍,它深入剖析了C++语言的核心——对象模型。这本书旨在帮助开发者更好地理解C++中的内存管理、类型系统、类层次结构以及对象生命周期等关键概念。...
《深度探索C++对象模型》是一本专注于C++底层机制的专著,主要针对2012年的标准进行深入解析。C++是一种多范式、静态类型、编译型、并发型、通用程序设计语言,它以其强大的功能和灵活性而闻名。这本书的目标是帮助...
《深度探索C++对象模型》专注于C++面向对象程序设计的底层机制,包括结构式语意、临时性对象的生成、封装、继承,以及虚拟——虚拟函数和虚拟继承。这本书让你知道:一旦你能够了解底层实现模型,你的程序代码将获得...
《深度探索C++对象模型》是侯捷翻译的一本关于C++对象模型深入探讨的专业书籍,原著作者为Stanley B. Lippman。这本书详细解析了C++中对象的内部表示、构造和析构的过程以及运行时行为等关键概念。它不仅仅是一本...
C++作为一种支持面向对象的编程语言,其对象模型则涉及到内存分配、对象的构造与析构、成员函数的调用、多态性、继承机制等多个方面。 本书《深度探索C++对象模型》(Inside The C++ Object Model)由Stanley B. ...
### 深度探索C++对象模型:理解与解析 #### C++对象模型概览 C++对象模型是C++编程语言中一个核心且复杂的概念,它定义了如何在内存中表示类、对象以及它们之间的关系。理解C++对象模型对于深入掌握C++语言特性、...
深度探索C++对象模型 超高清
深度探索C++对象模型
通过阅读《深度探索C++对象模型》,读者将能够理解C++如何在底层实现这些特性,掌握更深层次的编程技巧,提高代码质量,并能更好地优化和调试程序。此外,了解对象模型还有助于在设计复杂系统时做出明智的决策,确保...
深度探索C++对象模型 第0章 导读(译者的话) 第1章 关于对象(Object Lessons) 加上封装后的布局成本(Layout Costs for Adding Encapsulation) 1.1 C++模式模式(The C++ Object Model) 简单对象模型(A Simple...
深度探索c++对象模型.pdf Inside The C++ Object Model专注于C++对象导向程序设计的底层机制,包括结构式语意、暂时性对象的生成、封装、继承,以及虚拟——虚拟函数和虚拟继承。这本书让你知道:一旦你能够了解...
深度探索C++对象模型 中文图片影印版pdf,比较清晰,不是那种模糊的版本,和文字版差别不大 英文清晰文字版chm 第一代C++编译器开发主管所写。如果你想成为真正的C++高手,看这本书,他为你讲述了编译器在处理各种...
总结笔记,关于侯捷翻译的《深入探索c++对象模型》的笔记 作者Lippman参与设计了全世界第一套C++编译程序cfront,这本书就是一位伟大的C++编译程序设计者向你阐述他如何处理各种explicit(明确出现于C++程序代码中)...
深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象...