1

この質問はC++に関するものです

C ++での配列の名前は単なるポインタだといつも思っていたので、

int ar[10];
cout << sizeof(ar);

と同じように表示されsizeof(int *)ます。しかし、それは40を与えます-したがって、それは配列全体の実際のサイズです。また、配列サイズが変数で指定されている場合は40になります。

int n = 10;
int ar[n]

配列を含むクラスをコピーしたい。演算子で割り当てられる場合はnew、コピーコンストラクター内でこの配列を手動でコピーする必要があります。しかし、一定サイズの配列はどうですか?クラスには配列へのポインタのみが含まれていますか、それとも配列全体が含まれていますか?ここで簡単memcpy(...)に安全ですか?

編集:別の例:

int n;
cin >> n;
int ar[n];
cout << sizeof(ar);

n*4を出力します。Linuxでg++を使用しています。

私もこれを試しました:

class Test {
public:
    int ar[4];
};

Test a, b, c;
a.ar[0] = 10;
b = a;
memcpy(&c, &a, sizeof(a));
a.ar[0] = 20;
cout << "A: " << a.ar[0] << endl;
cout << "B: " << b.ar[0] << endl;
cout << "C: " << c.ar[0] << endl;

そしてそれは与える:

A: 20
B: 10
C: 10

したがって、配列はクラスの一部として格納され、memcpyを使用してコピーできます。しかし、それは安全ですか?

4

6 に答える 6

8

配列はポインタではありません。配列の名前は、関数にパラメーターとして渡すと、ポインターに「減衰」しますが、関数sizeofではなく言語に組み込まれた演算子であるためsizeof(array)、配列の実際のサイズは、実際の配列に適用されます(配列の名前がパラメーターとしてsizeof()渡され、関数に渡されたときに減衰したポインターを使用するのとは対照的です。

配列を含むクラスをコピーする限り、それが実際に配列である場合は、次のようになります。

class X { 
    int x[10];
};

そうすれば、コピーするために何もする必要はありません。コンパイラは、配列の内容をコピーするコピーコンストラクタを生成できます。実際にポインタがあり、自分でスペースを割り当てる場合は、「ディープコピー」を実行するためにコピーコンストラクタを作成する必要がありますか(つまり、新しいオブジェクトにスペースを割り当て、ポインタ)。ただし、これを行うのではなく、通常はを使用する必要がありますstd::vector。これはすべてを内部で行うため、心配する必要はありません。

于 2009-12-09T15:27:10.760 に答える
5

これらを一度に1つずつ取ってください:

配列を含むクラスをコピーしたい。

さて、例えば:

class Foo
{
    int arr[20];
};

演算子newで割り当てられる場合は、コピーコンストラクター内でこの配列を手動でコピーする必要があります。

さて、混乱が始まります。上記の例では、配列は実際にはオブジェクトの一部です。 が4バイトsizeof(Foo)の場合は80になります。int

別の方法は、配列へのポインターを持つことです。これは、配列のサイズを変更する必要がある場合に役立ちます。

class Bar
{
    int *arr;
};

その場合、sizeof(Bar)はポインタのサイズ(通常は4バイトまたは8バイト)であり、オブジェクトをコピーするとポインタがコピーされます。これは「浅いコピー」と呼ばれます。「ディープコピー」が必要な場合、つまり、コピーがオブジェクトだけでなく配列の内容を複製する場合は、コピーコンストラクターが必要です。

3番目の選択肢はvector、ウィーティーズが推奨するように、を使用することです。

class Bob
{
    std::vector<int> arr;
};

Barこれは内部的にはケースと同じように機能し、vectorサイズを変更できますが、vectorテンプレートがディープコピーを処理するため、コピーコンストラクターは必要ありません。

Fooコンパイル時にサイズがわかっている固定サイズの配列が必要な場合は、このケースをお勧めします。それ以外のBob場合は、このケースをお勧めします。ケースは、Barほとんど車輪の再発明です。

単純なmemcpy(...)はここで安全ですか?

安全ですFooBar浅いコピーが必要な場合に安全です。のために安全ではありませんBob

話の教訓は、変数をオブジェクトに格納することは、それを関数ブロックまたはグローバルに格納するのと同じように機能することです。配列を([N]ではなく*)指定する場合、サイズはコンパイル時に決定する方が適切であり、ストレージを取得します。そこに配置されます。

于 2009-12-09T15:32:53.570 に答える
2

コメント:標準C (C89)では、配列を初期化できるのはリテラルまたは定数のみです。したがって、コンパイラに混乱はありません。

回答:「スタックウェイ上」で配列を初期化する場合、つまり、newまたはmallocを使用しない場合。クラスは配列のサイズをカプセル化します。コンパイラは内部でポインタ演算を使用してアクセス演算子を解決するため、配列のベースはポインタ[]です。

newまたはmallocを使用する場合は、割り当てたメモリのサイズの決定的な尺度として、メモリの割り当てに使用した変数を使用する必要があります。配列ベースポインタで使用できますmemcpy()。割り当てられている場所に関係なく、配列のベースメモリ位置へのポインタのままです。

回答2:質問の編集後

はい、同じタイプで同じサイズのconstサイズの配列を使用してこれを行うことは完全に安全ですが、タイプとサイズを追跡している場合を除き、動的に割り当てられたリソースを使用してこれを行わないでください。あなたが取っているアプローチを避けて、コンテナクラスで行ってください。

注:sizeof動作が何であるかを言うことができないので、ヒープに割り当てられたメモリのオペレーターに関するあなたのポイントを回避しました。私は昔ながらの人で、動的リソースにsizeofを使用することはありません。これは、ランタイム機構に依存し、さまざまなコンパイラベンダーに含まれるランタイム機構を誰が知っているかを示しているためです。

ただし、C ++で(ほとんどのコンパイラで)newによって配列が割り当てられると、配列のベースに整数(物理的に)が配置されることを私は知っています。この整数は、後続の配列のサイズを表します。このため、演算子を使用する必要がありますdelete []。この演算子はdelete、コンパイラがループを吐き出し、アイテムのデストラクタを繰り返し呼び出すため、標準とは異なります。C ++コンパイラベンダーが整数を配列のベースに配置する場合、実行時にこれを使用してsizeof演算子用に整数を抽出する可能性があります。私はそれについて規格が何を言っているのか分かりません、そしてそれがこのように機能するのではないかと思います。

テスト:Visual Studio 2008

#include "stdafx.h"
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    int *y=new int[5];
    int z[5];
    std::cout << sizeof(x);
    std::cout << " " <<  sizeof(y);
    std::cout << " " << sizeof(z);
    return 0;
}

出力:

4 4 20

配列のサイズは、ハードコーディングされている場合、つまりzの場合のように、実行時にのみ決定できます。xはintであり、yはポインタであり、どちらもそのように動作します。

于 2009-12-09T15:10:51.140 に答える
2

実際arには配列であるため、sizeof()は実際のサイズを検出します。C / C ++の配列には、ポインター(ポインター式または右辺値)があります。

int ar[10];
int *p;
p = ar;  // OK
ar = p;  // error 
于 2009-12-09T15:16:50.443 に答える
1

sizeof(ar)の宣言がarその時点でコンパイラに表示されるためにのみ機能します。配列サイズは保存されず、実行時に使用できることはありません。関数に
渡してそこで実行すると、が取得されます。つまり、基本的には、コンパイラは上記の数行を見るのに十分賢いということです。arsizeof(ar)4

sizeofコンパイル時にサイズを提供するコンパイラキーワードです。したがって、コンパイラは、適切なサイズを与えるために、調べている変数のサイズを認識または推測できる必要があります。

再編集::はコンパイル時定数[ sizeofC99可変長配列を除く]であり、コンパイラーはランタイム計算を追加できます。ただし、C99はC ++標準の一部ではないため、これはC++のコンパイラ拡張です。

于 2009-12-09T15:10:39.670 に答える
1

STLのベクトルを調べましたか。これは配列と同様に動作し、サイズを調整可能で、配列のサイズを返す関数「size()」が含まれています。

于 2009-12-09T15:18:36.937 に答える