これは、行われたオンラインC++テストの質問です。
_#include<iostream>
using namespace std;
class A
{
};
class B
{
int i;
};
class C
{
void foo();
};
class D
{
virtual void foo();
};
class E
{
int i ;
virtual void foo();
};
class F
{
int i;
void foo();
};
class G
{
void foo();
int i;
void foo1();
};
class H
{
int i ;
virtual void foo();
virtual void foo1();
};
int main()
{
cout <<"sizeof(class A) : " << sizeof(A) << endl ;
cout <<"sizeof(class B) adding the member int i : " << sizeof(B) << endl ;
cout <<"sizeof(class C) adding the member void foo() : " << sizeof(C) << endl ;
cout <<"sizeof(class D) after making foo virtual : " << sizeof(D) << endl ;
cout <<"sizeof(class E) after adding foo virtual , int : " << sizeof(E) << endl ;
cout <<"sizeof(class F) after adding foo , int : " << sizeof(F) << endl ;
cout <<"sizeof(class G) after adding foo , int : " << sizeof(G) << endl ;
G g;
cout <<"sizeof(class G) after adding foo , int : " << sizeof(g) << endl ;
cout <<"sizeof(class H) after adding int 2 virtual " << sizeof(H) << endl ;
return 0;
}
_
出力:
_sizeof(class A) : 1
sizeof(class B) adding the member int i : 4
sizeof(class C) adding the member void foo() : 1
sizeof(class D) after making foo virtual : 8
sizeof(class E) after adding foo virtual , int : 16
sizeof(class F) after adding foo , int : 4
sizeof(class G) after adding foo , unsigned int : 4
sizeof(class g) after adding foo , unsigned int : 4
sizeof(class H) after adding int 2 virtual 16
_
私の質問:
siszeof(A)
が1で、sizeof(C)
も1であるのはなぜですか?
siszeof(H)
が16であるのに、sizeof(G)
が4であるのはなぜですか?
siszeof(E)
が16であるのに、sizeof(F)
が4であるのはなぜですか?
siszeof(D)
が8であるのに、sizeof(E)
が16であるのはなぜですか?
私の推測:
仮想関数は、8バイトのポインターです。しかし、なぜE
サイズが16なのかわかりませんか?空のクラスに関数を追加しても、そのサイズは変わりませんか?
どんな助けでも大歓迎です。
ありがとう
まず、仮想関数は8バイトのポインターではありません。 C++では、sizeof(char)
だけが任意のバイト数であることが保証されています。
次に、クラスの最初の仮想関数のみがサイズを大きくします(コンパイラーに依存しますが、すべてではないにしても、ほとんどの場合、このようになります)。以降のすべてのメソッドはそうではありません。非仮想関数はクラスのサイズに影響を与えません。
これは、クラスインスタンスがメソッド自体へのポインタを保持しておらず、クラスごとに1つである仮想関数テーブルへのポインタを保持しているために発生します。
だからあなたが持っていた場合:
_class A
{
virtual void foo();
}
_
そして
_class B
{
virtual void goo();
virtual void test();
static void m();
void x();
}
_
sizeof(A) == sizeof(B)
があります。
そして今:
Siszeof(A)が1で、sizeof(C)も1であるのはなぜですか?
A
とC
のサイズは、クラスのサイズが0であることが許可されていないという理由だけで、サイズ1です。関数はそれとは関係ありません。単なるダミーバイトです。
Siszeof(H)が16であるのに、sizeof(G)が4であるのはなぜですか?
G
には、メモリを説明するメンバーが1つだけあります-int
。そして、あなたのプラットフォームでは、sizeof(int) == 4
。 H
の他に、int
には、vftable
へのポインタもあります(仮想関数テーブル、上記を参照)。このサイズ、intのサイズ、および配置はコンパイラ固有です。
Siszeof(E)が16であるのに、sizeof(F)が4であるのはなぜですか?
上で説明した-非仮想メソッドはクラスのメモリを占有しません。
Siszeof(D)が8であるのに、sizeof(E)が16であるのはなぜですか?
D
には、プラットフォーム上で明らかに8バイトであるvftable
ポインタのみが含まれています。 E
にもintがあり、vftable
は8バイトに整列されます。つまり、次のようなものです。
_class E
4 bytes for int | 4 padding bytes | 8 bytes for vftable pointer |
| x | x | x | x | | | | | v | v | v | v | v | v | v | v |
_
Siszeof(A)が1で、sizeof(C)も1であるのはなぜですか?
C
の関数は仮想ではないため、クラスはvtableポインターを必要としないため、A
以上のストレージは必要ありません。 A
もC
もストレージをまったく必要としませんが、言語では同じクラスの異なるインスタンスが異なるポインターを持つ必要があるため、サイズをゼロにすることはできません-したがって、コンパイラーはそれらを作成しますできるだけ小さく、つまり1バイト。
Siszeof(H)が16であるのに、sizeof(G)が4であるのはなぜですか?
G
には仮想関数がないため、格納する必要があるのはintだけです。これは、コンパイラとアーキテクチャでは4バイトです。
H
には仮想関数があるため、クラスにはint
とvtableポインターが含まれている必要があります。広く使用されているすべてのコンパイラは、クラスの先頭にvtableポインタを格納するため、レイアウトは{vptr、int}です。これは、64ビットホストを使用している場合は8 + 4 = 12バイトです。
ただし、コンパイラはこれを16バイトまで自由に埋めることができるため、H
の複数のインスタンスが配列に割り当てられている場合、それらはすべてWordに揃えられます。ポインター(ここではvtableポインター)にアクセスする場合、Wordに整列されていない場合、パフォーマンスに重大な影響があるため、これは重要です。
Siszeof(E)が16であるのに、sizeof(F)が4であるのはなぜですか?
Eには仮想関数があるため、vtable ptrが必要です。したがって、そのレイアウトはH
と同じです。 F
には仮想関数がなく、intしかないため、レイアウトはG
と同じです。したがって、答えはG
およびH
の場合と同じです。
メンバー変数は1つだけであり、vtable ptrが存在する場合は常に最初に実行されるため、メンバー/関数の順序はここでは重要ではありません。
Siszeof(D)が8であるのに、sizeof(E)が16であるのはなぜですか?
D
にはメンバー変数はありませんが、仮想関数があるため、vtableポインターが必要です。必要なのはvtableポインターだけなので、サイズはsizeof(void*)
、つまり8バイトです。 E
はD
と同じで、整数の場合は4バイトが必要であり、コンパイラーはアライメントのために16バイトに切り上げます。
これは、サイズ0のクラス/構造体を禁止するC++標準のためです。そのため、空の構造体/クラスのサイズは1です。かなり煩わしいと思いますが、それにはいくつかの理由があります。
それはintのサイズで、プレーンでシンプルです:)
これは空の構造体のサイズです(Aを参照)。非仮想関数は、オブジェクトのサイズにはまったく影響しません。非仮想メンバー関数を呼び出すためにオブジェクトに格納する必要があるものは何もありません。
64ビットアプリケーションを構築していると想定しています。少なくとも1つの仮想関数を持つクラスには、仮想メソッドテーブルへのポインタがあります。これにより、オブジェクトポインタが親クラスにキャストされた場合でも、正しい仮想関数を呼び出すことができます。多くの場合、ポインタはvtableと呼ばれます。ウィキでもっと読む: http://en.wikipedia.org/wiki/Virtual_method_table
サイズ8はその64ビットポインタから来ていると思います。
ポインタと整数を格納するには、技術的に12バイトが必要です。ただし、ポインタは8バイトに揃える必要があります。ここで、オブジェクトEの配列Aを作成することを想像してください。A[0].vtable
のアドレスは&A + 0、A[0].i
の位置は&A+8
、A[1].vtable
の位置は&A+12
です。 -うわー、問題があります。12は8で割り切れません。そのため、コンパイラはパディングを作成します。不要なバイトを追加して、オブジェクトを配列内で正しく整列させます。この場合、8で割り切れる最小の数は16です。したがって、サイズです。
Cの場合と同じです。非仮想関数はサイズにまったく寄与しないため、サイズはBと一致します。
sizeof(G)== 4--Fと同じ
sizeof(H)== 16
仮想関数の数は関係ありません。オブジェクトにはまだ1つのvtableポインターしかありません。より多くの仮想関数を配置すると、仮想テーブルは大きくなりますが、オブジェクト自体は大きくなりません。多くのオブジェクト指向プログラムでは、多くの場合、多くの仮想関数が使用されます。オブジェクト自体にポインタを直接格納するのは無駄です。
そのため、Hのサイズ(および説明)はEのサイズと一致します。