Для объекта с нетривиальным конструктором обращение к любому нестатическому члену или базовому классу объекта до начала выполнения конструктора приводит к неопределенному поведению. Для объекта с нетривиальным деструктором обращение к любому нестатическому члену или базовому классу объекта после того, как деструктор завершает выполнение, приводит к неопределенному поведению. [ Example:
struct X { int i; }; struct Y : X { Y(); }; // non-trivial struct A { int a; }; struct B : public A { int j; Y y; }; // non-trivial extern B bobj; B* pb = &bobj; // OK int* p1 = &bobj.a; // undefined, refers to base class member int* p2 = &bobj.y.i; // undefined, refers to member's member A* pa = &bobj; // undefined, upcast to a base class type B bobj; // definition of bobj extern X xobj; int* p3 = &xobj.i; // OK, X is a trivial class X xobj;
Другой пример:
struct W { int j; };
struct X : public virtual W { };
struct Y {
int* p;
X x;
Y() : p(&x.j) { // undefined, x is not yet constructed
}
};
— end example ]
Чтобы явно или неявно преобразовать указатель (а glvalue) со ссылкой на объект класса X к указателю (ссылка) для прямого или косвенного базового класса B из X, строительства X и строительства всех своих прямых или косвенных оснований , которые прямо или косвенно derive from B должно начаться, и уничтожение этих классов не должно завершиться, в противном случае преобразование приведет к неопределенному поведению. Чтобы сформировать указатель на (или получить доступ к значению) прямого нестатического члена объекта obj, obj должно быть начато построение и его разрушение не должно завершаться, в противном случае вычисление значения указателя (или доступ к значению члена) приводит к неопределенному поведению. [ Example:
struct A { }; struct B : virtual A { }; struct C : B { }; struct D : virtual A { D(A*); }; struct X { X(A*); }; struct E : C, D, X { E() : D(this), // undefined: upcast from E* to A* might use path E* → D* → A* // but D is not constructed // “D((C*)this)” would be defined: E* → C* is defined because E() has started, // and C* → A* is defined because C is fully constructed X(this) {} // defined: upon construction of X, C/B/D/A sublattice is fully constructed };
— end example ]
Функции-члены, в том числе virtual functions, могут вызываться во время построения или разрушения ([class.base.init]). Когда виртуальная функция вызывается прямо или косвенно из конструктора или из деструктора, в том числе во время создания или уничтожения нестатических элементов данных класса, и объект, к которому применяется вызов, является объектом (вызовите его x) в процессе создания или разрушения, вызываемая функция является последним переопределителем в классе конструктора или деструктора, а не переопределяющим его в более производном классе. Если при вызове виртуальной функции используется явное class member access выражение, а объектное выражение относится к полному объекту x или одному из подобъектов базового класса этого объекта, но не к x одному из его подобъектов базового класса, поведение не определено. [ Example:
struct V { virtual void f(); virtual void g(); }; struct A : virtual V { virtual void f(); }; struct B : virtual V { virtual void g(); B(V*, A*); }; struct D : A, B { virtual void f(); virtual void g(); D() : B((A*)this, this) { } }; B::B(V* v, A* a) { f(); // calls V::f, not A::f g(); // calls B::g, not D::g v->g(); // v is base of B, the call is well-defined, calls B::g a->f(); // undefined behavior, a's type not a base of B }
— end example ]
typeid operator Могут быть использованы в процессе строительства или уничтожения ([class.base.init]). Когда typeid используется в конструкторе (включая mem-initializerили default member initializer для нестатического члена данных) или в деструкторе, или используется в функции, вызываемой (прямо или косвенно) из конструктора или деструктора, если операнд typeid относится к строящемуся объекту или уничтожение, typeid дает std::type_info объект, представляющий класс конструктора или деструктора. Если операнд typeid относится к строящемуся или разрушающемуся объекту, а статический тип операнда не является ни конструктором, ни классом деструктора, ни одной из его баз, поведение не определено.
dynamic_casts может использоваться во время строительства или разрушения ([class.base.init]). Когда a dynamic_cast используется в конструкторе (включая mem-initializerинициализатор члена или по умолчанию для нестатического члена данных) или в деструкторе, или используется в функции, вызываемой (прямо или косвенно) из конструктора или деструктора, если операнд элемента dynamic_cast ссылается для строящегося или разрушаемого объекта этот объект считается наиболее производным объектом, имеющим тип класса конструктора или деструктора. Если операнд dynamic_cast относится к объекту, находящемуся в процессе создания или уничтожения, а статический тип операнда не является указателем или объектом собственного класса конструктора или деструктора или одной из его баз, это dynamic_cast приводит к неопределенному поведению. [ Example:
struct V { virtual void f(); }; struct A : virtual V { }; struct B : virtual V { B(V*, A*); }; struct D : A, B { D() : B((A*)this, this) { } }; B::B(V* v, A* a) { typeid(*this); // type_info for B typeid(*v); // well-defined: *v has type V, a base of B yields type_info for B typeid(*a); // undefined behavior: type A not a base of B dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B results in B* dynamic_cast<B*>(a); // undefined behavior, a has type A*, A not a base of B }
— end example ]