2011-11-08 13 views
24

質問はかなり簡単です。明確にするために、以下の例を考えてみます。クラス・メソッドはクラス・インスタンスのサイズを大きくしますか?

// Note that none of the class have any data members 
// Or if they do have data members, they're of equal size, type, and quantity 
class Foo { 
public: 
    void foo1(); 
    void foo2(); 
    // 96 other methods ... 
    void foo99(); 
}; 

class Bar { 
public: 
    // Only one method 
    void bar(); 
}; 

class Derived1 : public Foo { }; 
class Derived2 : public Bar { }; 

int main() { 
    Foo f; 
    Bar b; 
    Derived1 d1; 
    Derived2 d2; 
    return 0; 
} 

はインスタンスfbd1を行い、そしてd2全てがメモリに同じ量の空間を占有?この質問の延長として、Fooのインスタンスをコピーすると、理論的にはBarよりも長くかかりますか?

+0

99方法? arrgh .. – Nim

+0

@ニム:私は本当にポイントを取得したかった。;) – Zeenobit

答えて

21

仮想関数を追加するか、仮想関数を持つクラスから継承すると、クラスのインスタンスのサイズが増加するという点を除いて、インスタンスデータだけがクラスのインスタンスのサイズを増やします(私が知っているすべての実装で) vテーブルポインタ。

また、他の誰かが正確に言えば、クラスの最小サイズは1バイトです。

いくつかの例:

// size 1 byte (at least) 
class cls1 
{ 
}; 

// size 1 byte (at least) 
class cls2 
{ 
    // no hit to the instance size, the function address is used directly by calling code. 
    int instanceFunc(); 
}; 

// sizeof(void*) (at least, for the v-table) 
class cls3 
{ 
    // These functions are indirectly called via the v-table, a pointer to which must be stored in each instance. 
    virtual int vFunc1(); 
    // ... 
    virtual int vFunc99(); 
}; 

// sizeof(int) (minimum, but typical) 
class cls4 
{ 
    int data; 
}; 

// sizeof(void*) for the v-table (typical) since the base class has virtual members. 
class cls5 : public cls3 
{ 
}; 

コンパイラの実装では、複数のV-テーブルポインタまたは他の他の方法で複数の仮想継承を処理することができるので、これらはあまりにもクラスの大きさに影響を持つことになります。

最後に、メンバーデータの配置オプションが影響を与える可能性があります。コンパイラーには、メンバー・データに指定されたバイト数の倍数である開始アドレスが必要であることを指定する、いくつかのオプションまたは#pragmaがあります。例えば、4つのバイト境界にアライメントとsizeof(int) = 4を仮定して:はい、彼らは意志

// 12 bytes since the offset of c must be at least 4 bytes from the offset of b. (assuming sizeof(int) = 4, sizeof(bool) = 1) 
class cls6 
{ 
    int a; 
    bool b; 
    int c; 
}; 
+0

近いが、空の型は通常、少なくとも1バイト必要です。これらを1バイト未満にするコンパイラはほとんどありません。 –

+4

@ MooingDuck 2つのオブジェクトが同じアドレスを持つことができないように、バイト・アドレス可能なマシン上に少なくとも1バイトでなければなりません。コンパイラは決定しません。 (Stroustrupはどこかでこれを書いていましたが、どこを覚えているのかは分かりません) –

+0

@SethCarnegie:はい、標準の_also_は、コンパイラは同じIO呼び出しとvolatileを実行する限り、同じパラメータで同じ順序で読み書きするため、GCCはプログラムの実行を変更しない限り、オブジェクトを1バイト未満にすることがあります。 –

4

strickly、これは実装に依存します。しかし、メソッドの数はクラスオブジェクトのサイズを変更すべきではありません。非仮想メソッドの場合、メソッドポインタに関連するオブジェクトメモリ内には何もありません。仮想メソッドがある場合、各オブジェクトにはvtableへの単一のポインタがあります。メソッドを追加するとvtableが大きくなりますが、ポインタのサイズは変わりません。

詳細情報:非仮想メソッドの場合、コンパイラは各クラスのメソッドポインタを追跡します。非仮想メソッドを呼び出すと、コンパイラは隠しパラメータとして、またはスタック上にオブジェクトを指すメソッドへのポインタを渡します。これはメソッドがそのオブジェクトを '知っている'方法で、thisポインタにアクセスしています。仮想メソッドの場合、メソッドポインタは実際にvtableへのインデックスなので、コンパイラはvtableの逆参照されたエントリにthisを渡します。

+0

非仮想メソッドの場合、コンパイラは各クラスのメソッドポインタを追跡します。これはスペースのために時間を犠牲にするトレードオフですか? – Djvu

+0

@Djvu、いいえ、非仮想メソッドは、時間と空間の両方でより効率的です。 – ThomasMcLeod

3

。実際には、サイズは1になります。C++では、データメンバーがなくても、クラスは1のサイズを持ちます。

1

FooおよびBarはそれぞれ1バイトです。 Derived1Derived2は1バイトまたは2バイトです。

なぜなら、コンパイラはすべてのメンバ関数を含む実行可能コードにすべての静的情報を格納しているからです。あなたのコードがFooのインスタンス上でfoo1を呼び出す場合、それはFoo::foo1(this)を呼び出します。これは、プログラム内のすべてFooで同じです。仮想関数は例外ですが、仮想関数があればメンバ関数ごとに追加のサイズは追加せず、1回の追加サイズ(通常は4/8バイト)のみを追加します。

関連する問題