忍者ブログ
[169] [168] [167] [166] [165] [164] [163] [162] [161] [160] [159]

DATE : 2024/04/27 (Sat)
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


DATE : 2006/09/24 (Sun)
Line line(new Point, new Point(1, 1));

Line line2 = line;

上のように変数 line2 を変数 line のオブジェクトで初期化したとき、line のオブジェクトが line2 に「コピー」されます。また、値渡しで関数にオブジェクトを渡す際や返す際にもオブジェクトは「コピー」されます。

Line line(new Point, new Point(1, 1));
Line line2(new Point(1, 1), new Point(2, 2));

line2 = line;

ちなみに、上のように宣言済みの変数 line2 に line のオブジェクトを代入する場合は、コピーではなく「代入」と呼ばれます。

オブジェクトのコピーや代入が行われると、コピー元のメンバ変数の値がコピー先のメンバ変数にコピーされます。

しかし、メンバ変数にポインタ変数がある場合には問題が発生します。例えば、次の Line クラスを考えてみます。

class Line {
private :
	const Point *start;
	const Point *end;

public :
	Line(const Point* start, const Point* end);
	~Line();
	const Point& getStart() const;
	const Point& getEnd() const;
};

Line::Line(const Point* start, const Point* end) :
	start(start), end(end) { }

Line::~Line() {
	delete this->start;
	delete this->end;
}

const Point& Line::getStart() const {
	return *(this->start);
}

const Point& Line::getEnd() const {
	return *(this->end);
}

ここで、次のようなプログラムを考えます。

void function(Line line) {
	// line に関する処理
}

int main() {
	Line line(new Point, new Point(1, 1));

	function(line);

	return 0;
}

関数 function は、Line オブジェクトを値渡しで行って処理する関数です。そこに、Line オブジェクトを渡すのが main 関数なのですが、このプログラムでは実行時にエラーが発生します。

というのも、関数 function に値渡しを行った際にはオブジェクトの「コピー」が行われるからです。コピーが行われると、変数 line のメンバ変数 line が引数 line にコピーされることになります。そのとき、Line オブジェクトの持っている Point オブジェクトへのポインタもコピーされることになります。

すると、変数 line と引数 line は別々の Line オブジェクトにもかかわらず、持っている Point オブジェクトへのポインタは同じ Point オブジェクトを指すことになります。

そして関数 function が終了すると、引数 line の Line オブジェクトは破棄されます。この時 Line クラスのデストラクタが呼ばれ、変数 line と引数 line が共有していた Point オブジェクトをも破棄してしまうことになります。

そこで、Line オブジェクトがコピーされる際には、Point オブジェクトも複製しなければなりません。この「コピー」される際に呼び出されるのが、「コピーコンストラクタ」です。

「コピーコンストラクタ」を追加した Line クラスを次に示します。

class Line {
private :
	const Point *start;
	const Point *end;

public :
	Line(const Point* start, const Point* end);
	Line(const Line& line);
	~Line();
	const Point& getStart() const;
	const Point& getEnd() const;
};

Line::Line(const Point* start, const Point* end) :
	start(start), end(end) { }

Line::Line(const Line& line) {
	const Point& start = line.getStart();
	const Point& end = line.getEnd();

	this->start = new Point(start.getX(), start.getY());
	this->end = new Point(end.getX(), end.getY());
}

Line::~Line() {
	delete this->start;
	delete this->end;
}

const Point& Line::getStart() const {
	return *(this->start);
}

const Point& Line::getEnd() const {
	return *(this->end);
}

ここで、次の部分に注目してください。

Line(const Line& line);
Line::Line(const Line& line) {
	const Point& start = line.getStart();
	const Point& end = line.getEnd();

	this->start = new Point(start.getX(), start.getY());
	this->end = new Point(end.getX(), end.getY());
}

自分と同じクラスのオブジェクトの const 付き参照を受け取るコンストラクタが「コピーコンストラクタ」です。このコンストラクタが、「コピー」の際に呼び出されます。

(;^ω^)ちなみに、上のコードの場合は、Point クラスの方にコピーコンストラクタを用意して、それを Line クラスから呼び出した方が綺麗かもしれません。例えば次のように、です。

class Point {
private :
	int x;
	int y;

public :
	Point();
	Point(int x, int y);
	Point(const Point& point);
	int getX() const;
	int getY() const;
};

Point::Point() : x(0), y(0) { }

Point::Point(int x, int y) : x(x), y(y) { }

Point::Point(const Point& point) : x(point.getX()), y(point.getY()) { }

int Point::getX() const {
	return this->x;
}

int Point::getY() const {
	return this->y;
}


class Line {
private :
	const Point *start;
	const Point *end;

public :
	Line(const Point* start, const Point* end);
	Line(const Line& line);
	~Line();
	const Point& getStart() const;
	const Point& getEnd() const;
};

Line::Line(const Point* start, const Point* end) :
	start(start), end(end) { }

Line::Line(const Line& line) :
		start(new Point(line.getStart())),
		end(new Point(line.getEnd())) {
}

Line::~Line() {
	delete this->start;
	delete this->end;
}

const Point& Line::getStart() const {
	return *(this->start);
}

const Point& Line::getEnd() const {
	return *(this->end);
}

(;^ω^)追加・変更した部分は次の通りです。

(Point クラス)

Point(const Point& point);
Point::Point(const Point& point) : x(point.getX()), y(point.getY()) { }

(Line クラス)

Line::Line(const Line& line) :
		start(new Point(line.getStart())),
		end(new Point(line.getEnd())) {
}

オブジェクトの代入については、次の記事に書こうと思います。

PR
●この記事にコメントする
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード
★無題
NAME: けい
ガッタイ!
2006/09/25(Mon)13:21:33 編集
●この記事へのトラックバック
この記事にトラックバックする:
忍者ブログ [PR]
ブログ内検索
最近の状況
リンク
カレンダー
03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
使用許諾
最新コメント
(08/15)
(05/04)
(03/06)
(03/04)
(09/25)
最新トラックバック
ブログ内検索
最近の状況
リンク
カレンダー
03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
使用許諾
最新コメント
(08/15)
(05/04)
(03/06)
(03/04)
(09/25)
最新トラックバック