4

この例は、Bruce Eckel の "Thinking in C++" の第 14 章、「アップキャストとコピー コンストラクター」から引用したものです。

#include <iostream>
using namespace std;

class Parent
{
    int i;

    public:
    Parent(int ii) : i(ii) { cout << "Parent(int ii)\n"; }
    Parent(const Parent& b) : i(b.i) {  cout << "Parent(const Parent&)\n"; }
    Parent() : i(0) { cout << "Parent()\n"; }
    friend ostream& operator<<(ostream& os, const Parent& b)
            { return os << "Parent: " << b.i << endl; }
};

class Member
{
    int i;

    public:
    Member(int ii) : i(ii) { cout << "Member(int ii)\n"; }
    Member(const Member& m) : i(m.i) { cout << "Member(const Member&)\n"; }
    friend ostream& operator<<(ostream& os, const Member& m)
            { return os << "Member: " << m.i << endl; }
};

class Child : public Parent
{
    int i;
    Member m;

    public:
    Child(int ii) : Parent(ii), i(ii), m(ii) { cout << "Child(int ii)\n"; }
    friend ostream& operator<<(ostream& os, const Child& c)
            { return os << (Parent&)c << c.m << "Child: " << c.i << endl; }
};

int main() {
  Child c(2);
  cout << "calling copy-constructor: " << endl;
  Child c2 = c;
  cout << "values in c2:\n" << c2;
}

著者は、このコードに関して次のコメントを作成します。

「子の operator<< は、その中の親部分の operator<< を呼び出す方法のために興味深いものです: Child オブジェクトを Parent& にキャストすることによって (参照の代わりに基本クラスオブジェクトにキャストする場合)通常、望ましくない結果が得られます):

return os << (Parent&)c << c.m << "Child: " << c.i << endl;

上記の命令を次のように置き換えて、プログラムも実行します。

return os << (Parent)c << c.m << "Child: " << c.i << endl;

プロプラムは問題なく実行されますが、予想される違いは 1 つだけです。ここで、Parentコピー コンストラクターが再度呼び出されて、引数cがにコピーされますParent::operator<<()

では、著者が話している望ましくない結果とは何でしょうか?

4

2 に答える 2

2

問題は、子を親(親ではなく)に難しいものとしてキャストすると、子を子にするすべてのものを単純に切り取ることになります。

通常、クラスに仮想関数がある場合(通常はクラス階層内にある場合)、(内部レイアウト、継承されたクラスの数などに応じて)vptrを変更できます。その後、次の領域に向かって移動します。未定義の動作。つまり、クラス階層で参照(またはポインター)を使用しないと、すべての魔法の継承メカニズム(ポリモーフィズムとも呼ばれます)が効果的に無効になります。

犬=飛行機と言うのと少し似ています。-そして、再解釈キャスト(これはCスタイルのキャストが効果的に行われるものです)を使用することにより、コンパイラからシャットダウンするように指示しているため、コンパイラから警告を発する機会があります。

于 2012-02-10T12:38:12.353 に答える
1

少し接線...

経験則: 基本クラスはコピー可能であってはならず、代わりに複製可能であるべきです。

実装: コピー コンストラクターとコピー代入演算子を無効にするか、(単純に) 純粋な仮想メソッドを作成します。

緩和: 純粋な仮想メソッドがない場合、基本クラスのコピー コンストラクターと代入演算子を作成する方が簡単protectedです。警告: これは、子クラスがその親のコピーを呼び出すことができるようになったことを意味します。これにより、スライスの問題が発生する可能性があります。

: C++11 では、これは対応する移動にも適用されます。

于 2012-02-10T12:59:10.587 に答える