Namespaces
Variants

Object

From ja.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

C++プログラムは、 objects を生成、破棄、参照、アクセス、操作します。

C++におけるオブジェクトは、

以下のエンティティはオブジェクトではありません:値、参照、関数、列挙子、型、非静的クラスメンバ、テンプレート、クラスまたは関数テンプレートの特殊化、名前空間、パラメータパック、および this 。

variable は、非静的データメンバーではないオブジェクトまたは参照であり、 declaration によって導入されます。

目次

オブジェクト生成

オブジェクトは明示的に、 定義 、 new 式 、 throw 式 、 union のアクティブメンバーの変更、および 一時オブジェクト を必要とする式の評価によって作成されます。作成されたオブジェクトは、明示的なオブジェクト作成において一意に定義されます。

暗黙の生存期間型のオブジェクトは、以下の方法でも暗黙的に作成できます

  • 定数評価中を除き、型 unsigned char または std::byte (C++17以降) の配列の生存期間を開始する操作において、その場合そのようなオブジェクトは配列内に作成され、
  • 以下の割り当て関数の呼び出しにおいて、その場合そのようなオブジェクトは割り当てられたストレージ内に作成されます:
(C++17以降)
  • 以下の関数への呼び出し オブジェクト表現 をコピーする場合、そのようなオブジェクトは宛先の記憶域領域または結果内で作成されます:
(C++20以降)
  • 以下の特定の関数の呼び出しにおいて、その場合、オブジェクトは指定されたストレージ領域に作成される:
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(C++23以降)

同じ記憶域の領域にゼロ個以上のオブジェクトが作成される可能性があります。これは、そのようにすることがプログラムに定義された動作を与える限りにおいて許されます。そのような作成が不可能な場合(例えば、競合する操作による場合)、プログラムの動作は未定義です。複数のそのような暗黙的に作成されたオブジェクトの集合がプログラムに定義された動作を与える場合、どのオブジェクトの集合が作成されるかは未規定です。言い換えれば、暗黙的に作成されるオブジェクトが一意に定義される必要はありません。

ストレージの指定された領域内でオブジェクトを暗黙的に作成した後、一部の操作は 適切に作成されたオブジェクト へのポインタを生成します。適切に作成されたオブジェクトは、ストレージの領域と同じアドレスを持ちます。同様に、プログラムに定義された動作を与えるポインタ値が存在しない場合にのみ動作は未定義となり、プログラムに定義された動作を与える複数の値が存在する場合、どのポインタ値が生成されるかは未規定です。

#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 があり、それらは以下の表で定義されています:

エンティティ オブジェクト表現 値表現
完全オブジェクト型 T T 型の非 ビットフィールド 完全オブジェクトが占める N 個の unsigned char オブジェクトのシーケンス( N は sizeof ( T ) に等しい) T 型の値を表現するために使用される T のオブジェクト表現内のビットの集合
T 型の非ビットフィールド完全オブジェクト obj T のオブジェクト表現に対応する obj のバイト列 T の値表現に対応する obj のビット列
ビットフィールドオブジェクト bf bf が占める N ビットのシーケンス( N はビットフィールドの幅) bf の値を表現するために使用される bf のオブジェクト表現内のビットの集合

型またはオブジェクトのオブジェクト表現において、値表現の一部ではないビットは パディングビット です。

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 と呼ばれます。

完全オブジェクト、メンバ部分オブジェクト、または配列要素が クラス型 である場合、その型は基底クラスの部分オブジェクトのクラス型と区別するために 最派生クラス と見なされます。最派生クラス型または非クラス型のオブジェクトは 最派生オブジェクト と呼ばれます。

クラスに対して、

その 潜在的に構築される部分オブジェクト と呼ばれます。

サイズ

サブオブジェクトは、それが基底クラスのサブオブジェクトである場合 または [[ no_unique_address ]] 属性で宣言された非静的データメンバーである場合 (C++20以降) 、 潜在的にオーバーラップするサブオブジェクト です。

オブジェクト obj がゼロサイズを持つ可能性があるのは、以下の条件がすべて満たされた場合に限ります:

  • obj は潜在的にオーバーラップする部分オブジェクトです。
  • obj は仮想メンバ関数と仮想基底クラスを持たないクラス型です。
  • obj は非ゼロサイズの部分オブジェクトや非ゼロ長の無名 bit-fields を持ちません。

上記のすべての条件を満たすオブジェクト obj について:

  • obj が非静的データメンバを持たない 標準レイアウト (C++11以降) クラス型の基底クラスサブオブジェクトである場合、そのサイズはゼロです。
  • それ以外の場合、 obj がゼロサイズとなる状況は実装定義です。

詳細については empty base optimization を参照してください。

ゼロ以外のサイズを持つ任意の非ビットフィールドオブジェクトは、1つ以上のバイトのストレージを占有しなければならず、そのサブオブジェクトのいずれかによって(完全にまたは部分的に)占有されるすべてのバイトを含みます。オブジェクトが trivially copyable または standard-layout (C++11以降) 型である場合、占有されるストレージは連続していなければなりません。

アドレス

オブジェクトがビットフィールドまたはサイズゼロのサブオブジェクトでない限り、そのオブジェクトの アドレス は、そのオブジェクトが占有する最初の byte のアドレスである。

オブジェクトは他のオブジェクトを含むことができ、その場合、含まれるオブジェクトは元のオブジェクト内に ネストされた 状態となります。以下の条件のいずれかを満たす場合、オブジェクト a は別のオブジェクト b 内にネストされていると言います:

  • a は b のサブオブジェクトです。
  • b ストレージを提供します ( a に対して)。
  • オブジェクト c が存在し、 a は c 内にネストされ、 c は b 内にネストされています。

オブジェクトは、以下のいずれかのオブジェクトである場合、 潜在的に非一意なオブジェクト となります:

(C++11以降)
  • 潜在的に非ユニークなオブジェクトのサブオブジェクト。

任意の2つの非ビットフィールドオブジェクトで、 生存期間 が重複する場合:

  • 以下のいずれかの条件が満たされる場合、それらは同じアドレスを持つ可能性があります:
  • 一方が他方の内部にネストされている場合。
  • いずれかがサイズゼロの副オブジェクトであり、それらの型が similar ではない場合。
  • 両方が潜在的に非ユニークなオブジェクトである場合。
  • それ以外の場合、それらは常に異なるアドレスを持ち、ストレージの互いに素なバイトを占有します。
// 文字リテラルは常に一意である
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機能( dynamic_cast および typeid )によって使用され、実行時に、オブジェクトが使用されている式に関係なく、そのオブジェクトが作成された型を決定します。

非多態オブジェクトの場合、値の解釈はオブジェクトが使用される式から決定され、コンパイル時に決定されます。

#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

厳格なエイリアシング

オブジェクトを、それが作成された型とは異なる型の式を使用してアクセスすることは、多くの場合未定義動作です。例外と例の一覧については reinterpret_cast を参照してください。

アライメント

すべてのオブジェクト型には、アライメント要件と呼ばれるプロパティがあります。これは非負整数値(std::size_t型で常に2のべき乗)であり、この型のオブジェクトが割り当てられる連続するアドレス間のバイト数を表します。

型のアライメント要件はalignofまたはstd::alignment_ofで照会できます。ポインタアライメント関数std::alignは、バッファ内で適切にアラインされたポインタを取得するために使用できます。std::aligned_storageは、適切にアラインされたストレージを取得するために使用できます。(C++23まで)

(C++11以降)

各オブジェクト型はその型のすべてのオブジェクトにアライメント要件を課します。より厳しい(より大きなアライメント要件を持つ)アライメントは、alignas(C++11以降)を使用して要求できます。アライメント要件を満たさないストレージにオブジェクトを作成しようとすると、未定義動作になります。

クラスのすべての非静的メンバのアライメント要件を満たすために、一部のメンバの後にパディングビットが挿入されることがあります。

#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

最も弱いアライメント(最小のアライメント要件)はchar、signed char、unsigned charのアライメントであり、1に等しい;スカラー型の最大の基本アライメントは実装定義であり、std::max_align_t(C++11以降)のアライメントに等しい。

特定のコンテキストでの特定の拡張アライメントの要求が実装によってサポートされていない場合、プログラムは不正形式です。

型のアライメントがstd::max_align_tよりも厳しく(大きく)された場合、その型は拡張アライメント要件を持つ型として知られています。アライメントが拡張された型、または非静的データメンバが拡張アライメントを持つクラス型は、オーバーアラインド型です。

アロケータ型は、オーバーアラインド型を正しく扱うことが要求されます。

(C++11以降)


new式と(C++17まで) std::get_temporary_bufferがオーバーアラインド型をサポートするかどうかは実装定義です。

(C++11以降)
(C++20まで)

注記

C++におけるオブジェクトは、 object-oriented programming (OOP) におけるオブジェクトとは異なる意味を持ちます:

C++におけるオブジェクト OOPにおけるオブジェクト
任意のオブジェクト型を持つことができる
( std::is_object を参照)
クラス型でなければならない
「インスタンス」の概念がない 「インスタンス」の概念がある(そして instanceof のような「インスタンス関係」を検出するメカニズムが存在する)
「インターフェース」の概念がない 「インターフェース」の概念がある(そしてインターフェースが実装されているかどうかを検出する instanceof のようなメカニズムが存在する)
仮想メンバーを介して明示的にポリモーフィズムを有効にする必要がある ポリモーフィズムは常に有効

欠陥報告書 P0593R6 において、バイト配列の作成時または allocation function (ユーザー定義であり constexpr である可能性がある)の呼び出し時に、implicit object creationが発生すると考えられていました。しかし、この許可は定数評価における非決定性を引き起こし、望ましくないものであり、いくつかの側面で実装不可能でした。その結果、 P2747R2 は定数評価におけるこのようなimplicit object creationを禁止しました。我々はこの変更を、論文全体がそうではないものの、意図的に欠陥報告として扱います。

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 633 C++98 変数はオブジェクトのみとされていた 参照も変数となりうる
CWG 734 C++98 同じスコープで定義され、同じ値を持つことが保証されている
変数が同じアドレスを持つかどうかは未規定であった
ライフタイムが重なる場合、値に関わらず
アドレスが異なることが保証される
CWG 1189 C++98 同じ型の2つの基底クラス部分オブジェクトが
同じアドレスを持つ可能性があった
常に異なるアドレスを
持つ
CWG 1861 C++98 狭い文字型のオーバーサイズビットフィールドの場合、
オブジェクト表現の全てのビットが
値表現に参加していた
パディングビットを許可
CWG 2489 C++98 char [ ] はストレージを提供できないが、オブジェクトは
そのストレージ内に暗黙的に作成されうる
オブジェクトは char [ ] のストレージ内に
暗黙的に作成できない
CWG 2519 C++98 オブジェクト表現の定義がビットフィールドに対応していなかった ビットフィールドに対応
CWG 2719 C++98 ミスアラインドされたストレージでのオブジェクト作成の
動作が不明確であった
この場合の動作は
未定義である
CWG 2753 C++11 初期化子リストのバッキング配列が
文字列リテラルとストレージを共有できるか不明確であった
ストレージを共有可能
CWG 2795 C++98 ライフタイムが重なる2つのオブジェクトが同じアドレスを
持つかどうかを決定する際、いずれかがゼロサイズの部分オブジェクトの場合、
それらは類似した異なる型を持つ可能性があった
非類似型のみを許可
P0593R6 C++98 以前のオブジェクトモデルは標準ライブラリが必要とする
多くの有用なイディオムをサポートせず、
Cの有効型(effective types)とも互換性がなかった
暗黙的オブジェクト作成が追加された

関連項目

**日本語訳:**
翻訳内容: - "C documentation" → "Cドキュメント" - "for" → "の"(文脈に合わせて自然な日本語表現に変換) - "Object" はC++用語として翻訳せずそのまま保持