20

C++ での基本的なポインターの理解をテストする次のコードがあります。

// Integer.cpp
#include "Integer.h"
Integer::Integer()
{
  value = new int;
  *value = 0;
}

Integer::Integer( int intVal )
{
  value = new int;
  *value = intVal;
} 

Integer::~Integer()
{
  delete value;
}

Integer::Integer(const Integer &rhInt)
{
  value = new int;
  *value = *rhInt.value;
}

int Integer::getInteger() const
{
  return *value;
}

void Integer::setInteger( int newInteger )
{
  *value = newInteger;
}

Integer& Integer::operator=( const Integer& rhInt )
{   
  *value = *rhInt.value;
  return *this;
}

// IntegerTest.cpp
#include <iostream>
#include <cstdlib>
#include "Integer.h"

using namespace std;

void displayInteger( char* str, Integer intObj )
{
  cout << str << " is " << intObj.getInteger() << endl;
}

int main( int argc, char* argv[] )
{
 Integer intVal1;
 Integer intVal2(10);

 displayInteger( "intVal1", intVal1 );
 displayInteger( "intVal2", intVal2 );

 intVal1 = intVal2;

 displayInteger( "intVal1", intVal1 );

 return EXIT_SUCCESS;
}

このコードは期待どおりに機能し、次のように出力されます。

intVal1 is 0

intVal2 is 10

intVal1 is 10

ただし、コピー コンストラクターを削除すると、次のように出力されます。

intVal1 is 0

intVal2 is 10

intVal1 is 6705152

なぜそうなのかわかりません。私の理解では、割り当てが存在しないオブジェクトに対するものである場合、コピー コンストラクターが使用されます。hereintVal1が存在するのに、代入演算子が呼び出されないのはなぜですか?

4

3 に答える 3

20

代入時にはコピー コンストラクターは使用されません。displayIntegerあなたのケースでは、引数を関数に渡すときにコピーコンストラクタが使用されます。2 番目のパラメーターは値で渡されます。つまり、コピー コンストラクターによって初期化されます。

コピー コンストラクターのバージョンは、クラスが所有するデータのディープコピーを実行します (代入演算子と同様)。したがって、すべてがコピー コンストラクターのバージョンで正しく動作します。

独自のコピー コンストラクターを削除すると、コンパイラーによって暗黙的にコピー コンストラクターが生成されます。コンパイラによって生成されたコピー コンストラクターは、オブジェクトの浅いコピーを実行します。これは「3 つのルール」に違反し、クラスの機能を破壊します。これはまさに実験で観察したことです。基本的に、最初の呼び出しでオブジェクトにdisplayIntegerダメージを与えintVal1、2 番目の呼び出しでオブジェクトにdisplayIntegerダメージを与えますintVal2。その後、両方のオブジェクトが壊れているため、3 番目のdisplayInteger呼び出しでゴミが表示されます。

の宣言をdisplayIntegerto に変更すると

void displayInteger( char* str, const Integer &intObj )

明示的なコピー コンストラクターがなくても、コードは "機能" します。しかし、どのような場合でも「3 つのルール」を無視するのは得策ではありません。この方法で実装されたクラスは、「Rule of Three」に従うか、コピー不可にする必要があります。

于 2013-09-23T21:26:44.637 に答える
3

この呼び出しについて考えてみましょう:

displayInteger( "intVal1", intVal1 );

intVal1intObjパラメータにのコピーを作成していますdisplayInteger:

void displayInteger( char* str, Integer intObj )
{
  cout << str << " is " << intObj.getInteger() << endl;
}

intそのコピーは同じものを指していintVal1ます。がdisplayInteger返されると、intObjが破棄されます。これにより、 がint破棄され、ポインタがintVal1無効なオブジェクトを指します。その時点で、値にアクセスしようとすると、すべての賭けがオフになります (AKA 未定義の動作)。についても同様のことが起こりintVal2ます。

より一般的なレベルでは、コピー コンストラクターを削除することにより、通常、この種の問題につながる 3 つのルールに違反します。

于 2013-09-23T21:33:51.427 に答える