Inside the C++ Object Model 读书笔记(二)

The Semantics of Constructors

2.1 Default Constructor Construction

The C++ Annotated Reference Manual (ARM) [ELLIS90] tells us that “default constructors…are generated (by the compiler) where needed….”

Global objects are guaranteed to have their associated memory “zeroed out” at program start-up. Local objects allocated on the program stack and heap objects allocated on the free-store do not have their associated memory zeroed out; rather, the memory retains the arbitrary bit pattern of its previous use.

If there is no user-declared constructor for class X, a default constructor is implicitly declared…. A constructor is trivial if it is an implicitly declared default constructor….

A nontrivial default constructor is one that in the ARM’s terminology is needed by the implementation and, if necessary, is synthesized by the compiler( This synthesis, however, takes place only if the constructor actually needs to be invoked.). The next four sections look at the four conditions under which the default constructor is nontrivial.

1. Member Class Object with Default Constructor (user defined constructor)

An interesting question, then: Given the separate compilation model of C++, how does the compiler prevent synthesizing multiple default constructors, for example, one for file A.C and a second for file B.C? In practice, this is solved by having the synthesized default constructor, copy constructor, destructor, and/or assignment copy operator defined as inline

class Foo { public: Foo(), Foo( int ) ... }; 

class Bar { public: Foo foo; char *str; }; 

void foo_bar() { 
   Bar bar; // Bar::foo must be initialized here 
   if ( str ) { } ... 
} 

The synthesized default constructor here is nontrivial.

2. Base Class with Default Constructor

Similarly, if a class without any constructors is derived from a base class containing a default constructor, the default constructor for the derived class is considered nontrivial and so needs to be synthesized.

3. Class with a Virtual Function

A virtual function table is generated for that class. Within each class object, the vptr is synthesized to hold the address of the associated class vtbl. The compiler must initialize the vptr of each object.

4. Class with a Virtual Base Class

Virtual base class implementations vary widely across compilers.

class X { public: int i; }; 
class A : public virtual X   { public: int j; }; 
class B : public virtual X   { public: double d; }; 
class C : public A, public B { public: int k; }; 
// cannot resolve location of pa->X::i at compile-time 
void foo( const A* pa ) { pa->i = 1024; } 

main() { 
   foo( new A ); 
   foo( new C ); 
   // ... 
} 

the compiler cannot fix the physical offset of X::i accessed through pa within foo()

    // possible compiler transformation 
void foo( const A* pa ) { pa->__vbcX->i = 1024; } 

The initialization of __vbcX (or whatever implementation mechanism is used) is accomplished during the construction of the class object.

++The Standard refers to these as implicit, nontrivial default constructors.++

  • Common misunderstandings:
    • That a default constructor is synthesized for every class that does not define one

    • That the compiler-synthesized default constructor provides explicit default initializers for each data member declared within the class

2.2 Copy Constructor

Bitwise Copy Semantics—Not!

There are four instances:

  1. When the class contains a member object of a class for which a copy constructor exists (either explicitly declared by the class designer, as in the case of the previous String class, or synthesized by the compiler, as in the case of class Word)
  2. When the class is derived from a base class for which a copy constructor exists (again, either explicitly declared or synthesized)
  3. When the class declares one or more virtual functions
  4. When the class is derived from an inheritance chain in which one or more base classes are virtual
Resetting the Virtual Table Pointer

when a class declare one or more virtual fucntions:

  1. A virtual function table that contains the address of each active virtual function associated with that class (the vtbl) is generated.
  2. A pointer to the virtual function table is inserted within each class object (the vptr).

To reset vptr, compiler will synthesize a copy constructor in order to properly initialize the vptr.

Handling the Virtual Base Class Subobject

Each implementation’s support of virtual inheritance involves the need to make each virtual base class subobject’s location within the derived class object available at runtime. Maintaining the integrity of this location is the compiler’s responsibility. Bitwise copy semantics could result in a corruption of this location, so the compiler must intercede with its own synthesized copy constructor.

2.3 Program Transformation Semantics

Argument Initialization

The Standard states (Section 8.5) that passing a class object as an argument to a function (or as that function’s return value) is equivalent to the following form of initialization:

X xx = arg; 

an invocation of the form

X xx; 
// ... 
foo( xx ); 

would be transformed as follows:

// Pseudo C++ code 
// compiler generated temporary 
X __temp0; 

// compiler invocation of copy constructor 
__temp0.X::X ( xx ); 

// rewrite function call to take temporary 
foo( __temp0 );     //The declaration of foo() therefore must also be transformed void foo( X& x0 )

An alternative implementation is to copy construct the actual argument directly onto its place within the function’s activation record on the program stack. Prior to the return of the function, the local object’s destructor, if defined, is applied to it. The Borland C++ compiler, for example, implements this strategy.

Return Value Initialization
  1. Add an additional argument of type reference to the class object. This argument will hold the copy constructed “return value.”
  2. Insert an invocation of the copy constructor prior to the return statement to initialize the added argument with the value of the object being returned.
X bar() 
{ 
   X xx; 
   // process xx ... 
   return xx; 
}

transformed to

// function transformation to reflect 
// application of copy constructor 
// Pseudo C++ Code 
void 
bar( X& __result ) 
{ 
   X xx; 

   // compiler generated invocation 
   // of default constructor 
   xx.X::X(); 

   // ... process xx 
   // compiler generated invocation 
   // of copy constructor 
   __result.X::X( xx ); 

   return; 
} 
X xx = bar(); 


transform to :

X xx;   //Note: no constructor applied
bar( xx ); 

______________
bar().memfunc(); 

transformed to:
// compiler generated temporary 
X __temp0; 
( bar( __temp0 ), __temp0 ).memfunc(); 
Optimization

NRV: it is possible for the compiler itself to optimize the function by substituting the result argument for the named return value.

void 
bar( X &__result ) 
{ 
   // default constructor invocation 
   // Pseudo C++ Code 
   __result.X::X(); 

   // ... process in __result directly 

   return; 
} 

wheather or not to use NRV depends on compiler.

Member Initialization List

the compiler iterates over and possibly reorders the initialization list to reflect the declaration order of the members. It inserts the code within the body of the constructor prior to any explicit user code.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值