仮想基底クラスをもつクラスの代入演算で、仮想基底クラスの代入が各々のサブオブジェクトにつきただ一回なされるようにしたい場合に、みなさん、どのように設計されていますか?
何か、手軽なテクニックがあれば、ご教示ください。
===(コンストラクタなどは略、アクセス制御も略)
たとえば、
struct X { int x; };
struct A : virtual X { int a; };
struct B : virtual X { int b; };
struct C : A, B { int c; };
int main()
{
C c1, c2;
c1 = c2;
}
として、デフォルト代入に頼ると、c1.x への c2.x の代入を複数回行うコンパイラが多いですよね(というか、きっちり1回のみ行うようにしているコンパイラに出会ったことがない)。C++標準でも、初期化はただ一度とコンパイラが保証するようになっていますが、代入は何度代入してもいいようになっていたと思います。
以下のように、各クラスで、直接の非仮想基底クラスと自身のデータメンバにのみ代入するメンバ関数を定義して、最派生クラスでのみ、仮想基底クラスの代入を行うのが、普通のやり方なんでしょうか。
これでもいいのですが、後に、ある基底クラスに仮想クラスが付け加わったりすると、その基底クラスから派生しているクラスの代入演算の定義をすべていじらないといけなくなるので、もっといいテクニックがあるのなら、と思いまして質問したしだいです。
struct X {
int x;
X &nvAssign(const X &z) { x = z.x; return *this; }
X &operator=(const X &z) { nvAssign(z); }
};
struct A : virtual X
{
int a;
A &nvAssign(const A &z) { a = z.a; return *this; }
A &operator=(const A &z) { X::nvAssign(z); return nvAssign(z); }
};
struct B : virtual X
{
int b;
B &nvAssign(const B &z) { b = z.b; return *this; }
B &operator=(const B &z) { X::nvAssign(z); return nvAssign(z); }
};
struct C : A, B
{
int c;
C &nvAssign(const C &z) {
A::nvAssign(z); B::nvAssign(z); c = z.c; return *this;
}
C &operator=(const C &z) { X::nvAssign(z); return nvAssign(z); }
};
===
1. 仮想基底クラスは何度代入されてもいいようにするもんだ。
2. 仮想基底クラスにデータメンバを持たせるのは間違いだ。
3. 遅くなるから、そもそも仮想基底クラスなど使わない。
という回答は無しでお願いいたします。たぶん、1 か 2 が正解なんでしょうけど^^
No.1ベストアンサー
- 回答日時:
コピー代入演算子は、コピーコンストラクタを用いて実装するのが基本です。
ですから、C& C::operator=(const C& src)
{
C temp(src);
this->swap(temp);
return *this;
}
のようにすれば、代入演算子の中で行われているのは、実は初期化ですので、コピーが一回しか発生しないことが保証されます。
なお、swapはオブジェクトの値を交換するためのメンバ関数で、原則として決して例外を送出しないように実装すべきものです。仮想継承にまつわる工夫(仮想基底クラスの部分オブジェクトの交換を1回しか行わないなど)は、このswapメンバ関数に集約しておくと後々楽です。
こうすることで、複数コピーされる問題が解消するほか、代入演算子の「強い例外安全保障」、そして「自己代入対策」も同時に得られます。
(データメンバがint型ならあまり関係ないでしょうが、一般的にはこれらは重要になります)
この回答への補足
そうかそうか。ブール値を swap() につけておけば、あまり仮想基底を気にしないですみますね。代償は、処理量が増えることですが。。。。でも、まあ、jacta さんが提示してくれたテクニックでプログラミングは楽になります^^ ありがとうございました。
====
#include <iostream>
#include <algorithm>
struct X {
int x;
X() {}
X(const X &z) { x = z.x; }
void swap(X &z, bool top) throw() { std::swap(x, z.x); }
X &operator=(const X &z) { X t(z); swap(t, true); return *this; }
};
struct A : virtual X
{
int a;
A() {}
A(const A &z) : X(z) { a = z.a; }
void swap(A &z, bool top) throw() {
if (top) X::swap(z, false);
std::swap(a, z.a);
}
A &operator=(const A &z) { A t(z); swap(t, true); return *this; }
};
struct B : virtual X
{
int b;
B() {}
B(const B &z) : X(z) { b = z.b; }
void swap(B &z, bool top) throw() {
if (top) X::swap(z, false);
std::swap(b, z.b);
}
B &operator=(const B &z) { B t(z); swap(t, true); return *this; }
};
struct C : A, B
{
int c;
C() {}
C(const C &z) : X(z), A(z), B(z) { c = z.c; }
void swap(C &z, bool top) throw() {
if (top) X::swap(z, false);
A::swap(z, false); B::swap(z, false); std::swap(c, z.c);
}
C &operator=(const C &z) { C t(z); swap(t, true); return *this; }
};
なるほど。アロケートしたメモリのポインタなどのガードには便利かもしれないですね。
swap() を定義するのに、仮想継承のオブジェクトの複数回の swap() を防止するのには、プログラマが注意していないとならないのですかね、やっぱり。。それに、コンストラクトしてスワップするので、2回分の代入処理量くらいになっちゃいますね^^
でも、興味あるテクニックです。ありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- C言語・C++・C# 変数のスコープ 5 2023/05/27 17:50
- C言語・C++・C# Cのdoubleの浮動小数点表示について 3 2023/04/17 13:14
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/06 09:28
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/17 18:41
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VBAのプログラムで、DIAG = 1# ...
-
Integer変数をカラにしたいので...
-
構造体のデータを丸ごとコピー...
-
VBAにてcolorindexを変数に格納...
-
プログラミング言語の変数と数...
-
値が変わるのはどうしてでしょ...
-
値が代入されてない時
-
VB6.0の変数、関数の定義位置か...
-
VBAの変数のデータ型を変更する...
-
long型のデータをバイト型の配...
-
C言語 構造体の中に共用体を定...
-
整数から16進数への変換 現在c...
-
ヘッダファイルと構造体
-
構造体の代入と比較
-
winsockのsendtoで送れるデータ型
-
C++ 構造体の一括初期化 {0}
-
構造体のポインタにNULLが入らない
-
セグメントエラー
-
関数から配列を返すには?
-
ExcelVBAで質問です。離れた二...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBAのプログラムで、DIAG = 1# ...
-
Integer変数をカラにしたいので...
-
C++ 構造体の一括初期化 {0}
-
long型のデータをバイト型の配...
-
「#undef」と「#define」の使い...
-
構造体のデータを丸ごとコピー...
-
VBAにてcolorindexを変数に格納...
-
C言語 構造体の中に共用体を定...
-
値が代入されてない時
-
異なる構造体のデータのコピー
-
typedefをプログラム中で解除す...
-
構造体のポインタにNULLが入らない
-
charとucharの違い
-
整数から16進数への変換 現在c...
-
VBAの変数のデータ型を変更する...
-
VB.NETのStructureというのはど...
-
構造体を型の異なる構造体に代入
-
日付チェック関数について
-
ユーザー定義型変数の一括初期化
-
構造体の初期化方法について
おすすめ情報