Object
C++プログラムは、 objects を生成、破棄、参照、アクセス、操作します。
C++におけるオブジェクトは、
-
サイズ(
sizeofで決定可能); -
アライメント要件(
alignofで決定可能); - ストレージ期間 (自動、静的、動的、スレッドローカル);
- 生存期間 (ストレージ期間または一時オブジェクトによって制限される);
- 型 ;
- 値(不定の場合あり、例: デフォルト初期化 された非クラス型);
- オプションで 名前 。
以下のエンティティはオブジェクトではありません:値、参照、関数、列挙子、型、非静的クラスメンバ、テンプレート、クラスまたは関数テンプレートの特殊化、名前空間、パラメータパック、および this 。
variable は、非静的データメンバーではないオブジェクトまたは参照であり、 declaration によって導入されます。
目次
オブジェクト生成オブジェクトは明示的に、 定義 、 new 式 、 throw 式 、 union のアクティブメンバーの変更、および 一時オブジェクト を必要とする式の評価によって作成されます。作成されたオブジェクトは、明示的なオブジェクト作成において一意に定義されます。 暗黙の生存期間型のオブジェクトは、以下の方法でも暗黙的に作成できます
同じ記憶域の領域にゼロ個以上のオブジェクトが作成される可能性があります。これは、そのようにすることがプログラムに定義された動作を与える限りにおいて許されます。そのような作成が不可能な場合(例えば、競合する操作による場合)、プログラムの動作は未定義です。複数のそのような暗黙的に作成されたオブジェクトの集合がプログラムに定義された動作を与える場合、どのオブジェクトの集合が作成されるかは未規定です。言い換えれば、暗黙的に作成されるオブジェクトが一意に定義される必要はありません。 ストレージの指定された領域内でオブジェクトを暗黙的に作成した後、一部の操作は 適切に作成されたオブジェクト へのポインタを生成します。適切に作成されたオブジェクトは、ストレージの領域と同じアドレスを持ちます。同様に、プログラムに定義された動作を与えるポインタ値が存在しない場合にのみ動作は未定義となり、プログラムに定義された動作を与える複数の値が存在する場合、どのポインタ値が生成されるかは未規定です。 #include <cstdlib> struct X { int a, b; }; X* MakeX() { // 可能な定義済み動作の1つ: // std::mallocの呼び出しは暗黙的に型Xのオブジェクトと // そのサブオブジェクトaおよびbを作成し、そのXオブジェクトへのポインタを返す X* p = static_cast<X*>(std::malloc(sizeof(X))); p->a = 1; p->b = 2; return p; } std::allocator::allocate への呼び出し、または union 型の暗黙的に定義されたコピー/ムーブ特殊メンバ関数によってもオブジェクトを生成できます。 オブジェクト表現と値表現一部の型とオブジェクトには object representations と value representations があり、それらは以下の表で定義されています:
型またはオブジェクトのオブジェクト表現において、値表現の一部ではないビットは パディングビット です。 TriviallyCopyable 型の場合、値表現はオブジェクト表現の一部であり、ストレージ内のオブジェクトが占有するバイト列をコピーするだけで同じ値を持つ別のオブジェクトを生成できることを意味します(ただし、オブジェクトが潜在的にオーバーラップする部分オブジェクトである場合、またはその値が型の トラップ表現 であり、CPUにロードするとハードウェア例外(SNaN(シグナリング非数)浮動小数点値やNaT(非実在)整数など)が発生する場合を除く)。 ほとんどの実装ではトラップ表現、パディングビット、または整数型の複数表現は許可されていませんが、例外があります。例えばItanium上の整数型の値は トラップ表現となる可能性があります 。 逆は必ずしも真ならず:同じ値を持つ TriviallyCopyable 型の二つのオブジェクトが異なるオブジェクト表現を持つ場合がある。例えば、複数の浮動小数点ビットパターンが同じ特殊値 NaN を表現し得る。より一般的には、パディングビットが アライメント要件 や ビットフィールド のサイズなどを満たすために導入されることがある。 #include <cassert> struct S { char c; // 1バイトの値 // 3バイトのパディングビット(alignof(float) == 4と仮定) float f; // 4バイトの値(sizeof(float) == 4と仮定) bool operator==(const S& arg) const // 値ベースの等価性 { return c == arg.c && f == arg.f; } }; void f() { assert(sizeof(S) == 8); S s1 = {'a', 3.14}; S s2 = s1; reinterpret_cast<unsigned char*>(&s1)[2] = 'b'; // 一部のパディングビットを変更 assert(s1 == s2); // 値は変更されていない } char 型、 signed char 型、および unsigned char 型のオブジェクトについて(過大サイズの ビットフィールド でない限り)、オブジェクト表現のすべてのビットは値表現に参加することが要求され、可能な各ビットパターンは異なる値を表します(パディングビット、トラップビット、または複数の表現は許可されません)。 サブオブジェクトオブジェクトは subobjects を持つことができます。これらには以下が含まれます
他のオブジェクトの部分オブジェクトではないオブジェクトは、 complete object と呼ばれます。 完全オブジェクト、メンバ部分オブジェクト、または配列要素が クラス型 である場合、その型は基底クラスの部分オブジェクトのクラス型と区別するために 最派生クラス と見なされます。最派生クラス型または非クラス型のオブジェクトは 最派生オブジェクト と呼ばれます。 クラスに対して、 その 潜在的に構築される部分オブジェクト と呼ばれます。 サイズ
サブオブジェクトは、それが基底クラスのサブオブジェクトである場合
または
オブジェクト obj がゼロサイズを持つ可能性があるのは、以下の条件がすべて満たされた場合に限ります:
上記のすべての条件を満たすオブジェクト obj について:
詳細については empty base optimization を参照してください。 ゼロ以外のサイズを持つ任意の非ビットフィールドオブジェクトは、1つ以上のバイトのストレージを占有しなければならず、そのサブオブジェクトのいずれかによって(完全にまたは部分的に)占有されるすべてのバイトを含みます。オブジェクトが trivially copyable または standard-layout (C++11以降) 型である場合、占有されるストレージは連続していなければなりません。 アドレスオブジェクトがビットフィールドまたはサイズゼロのサブオブジェクトでない限り、そのオブジェクトの アドレス は、そのオブジェクトが占有する最初の byte のアドレスである。 オブジェクトは他のオブジェクトを含むことができ、その場合、含まれるオブジェクトは元のオブジェクト内に ネストされた 状態となります。以下の条件のいずれかを満たす場合、オブジェクト a は別のオブジェクト b 内にネストされていると言います:
オブジェクトは、以下のいずれかのオブジェクトである場合、 潜在的に非一意なオブジェクト となります:
任意の2つの非ビットフィールドオブジェクトで、 生存期間 が重複する場合:
// 文字リテラルは常に一意である static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // 常にtrue // 「r」、「s」、「il」からアクセスされる文字'x'は // 同じアドレスを持つ可能性がある(つまり、これらのオブジェクトはストレージを共有する可能性がある) static const char (&r) [] = "x"; static const char *s = "x"; static std::initializer_list<char> il = {'x'}; const bool b2 = r != il.begin(); // 未規定の結果 const bool b3 = r != s; // 未規定の結果 const bool b4 = il.begin() != &test1; // 常にtrue const bool b5 = r != &test1; // 常にtrue ポリモーフィックオブジェクト
少なくとも1つの仮想関数を宣言または継承するクラス型のオブジェクトは、ポリモーフィックなオブジェクトです。各ポリモーフィックなオブジェクト内では、実装は追加情報(既存のすべての実装では、最適化されない限り1つのポインタ)を保存します。これは、
仮想関数
呼び出しおよびRTTI機能(
非多態オブジェクトの場合、値の解釈はオブジェクトが使用される式から決定され、コンパイル時に決定されます。
このコードを実行
#include <iostream> #include <typeinfo> struct Base1 { // polymorphic type: declares a virtual member virtual ~Base1() {} }; struct Derived1 : Base1 { // polymorphic type: inherits a virtual member }; struct Base2 { // non-polymorphic type }; struct Derived2 : Base2 { // non-polymorphic type }; int main() { Derived1 obj1; // object1 created with type Derived1 Derived2 obj2; // object2 created with type Derived2 Base1& b1 = obj1; // b1 refers to the object obj1 Base2& b2 = obj2; // b2 refers to the object obj2 std::cout << "Expression type of b1: " << typeid(decltype(b1)).name() << '\n' << "Expression type of b2: " << typeid(decltype(b2)).name() << '\n' << "Object type of b1: " << typeid(b1).name() << '\n' << "Object type of b2: " << typeid(b2).name() << '\n' << "Size of b1: " << sizeof b1 << '\n' << "Size of b2: " << sizeof b2 << '\n'; } 出力例: Expression type of b1: Base1 Expression type of b2: Base2 Object type of b1: Derived1 Object type of b2: Base2 Size of b1: 8 Size of b2: 1 厳格なエイリアシング
オブジェクトを、それが作成された型とは異なる型の式を使用してアクセスすることは、多くの場合未定義動作です。例外と例の一覧については
アライメントすべてのオブジェクト型には、アライメント要件と呼ばれるプロパティがあります。これは非負整数値(std::size_t型で常に2のべき乗)であり、この型のオブジェクトが割り当てられる連続するアドレス間のバイト数を表します。
各オブジェクト型はその型のすべてのオブジェクトにアライメント要件を課します。より厳しい(より大きなアライメント要件を持つ)アライメントは、 クラスのすべての非静的メンバのアライメント要件を満たすために、一部のメンバの後にパディングビットが挿入されることがあります。 このコードを実行 #include <iostream>
// objects of type S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
char a; // size: 1, alignment: 1
char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
// objects of type X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
int n; // size: 4, alignment: 4
char c; // size: 1, alignment: 1
// three bytes of padding bits
}; // size: 8, alignment: 4
int main()
{
std::cout << "alignof(S) = " << alignof(S) << '\n'
<< "sizeof(S) = " << sizeof(S) << '\n'
<< "alignof(X) = " << alignof(X) << '\n'
<< "sizeof(X) = " << sizeof(X) << '\n';
}
出力例: alignof(S) = 1
sizeof(S) = 2
alignof(X) = 4
sizeof(X) = 8
最も弱いアライメント(最小のアライメント要件)は 特定のコンテキストでの特定の拡張アライメントの要求が実装によってサポートされていない場合、プログラムは不正形式です。
注記C++におけるオブジェクトは、 object-oriented programming (OOP) におけるオブジェクトとは異なる意味を持ちます:
欠陥報告書
P0593R6
において、バイト配列の作成時または
allocation function
(ユーザー定義であり
不具合報告以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
関連項目
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||