0

私はbase-from-memberイディオムを使用していますが、現在はそれらのコピー/移動コンストラクターを使用しています。次のコードを想定します。

#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>

using namespace std;

struct A // base class
{
    A(string &s) : s(s), c(0) { };
    A(const A &a) : s(a.s), c(a.c) { };
    void print() { cout << s << endl; c++; }
    int print_count() { return c; }

    string &s;
    int c;
};

struct B_base // this class will be initialized before A
{
    B_base(int i)
    {
        s = boost::lexical_cast<string>(i);
    }

    B_base(const B_base &other) : s(other.s) { };

    string s;
};

struct B : B_base, A // main class
{
    B(int i) : B_base(i), A(B_base::s) { }
    B(const B &other) : B_base(other), A(other) { } // <-- problem here 

    using A::print;
    using A::print_count;
};


int main()
{
    B b(10);
    b.print(); // prints '10'
    cout << b.print_count() << endl; // prints '1'


    B b1(b);
    b1.print(); // prints '10'

    A &a = b;
    a.s =  "FAIL"; // we modify b, not b1 here!

    b1.print(); // but b1 prints 'FAIL' here --- error (it should still print '10')
    cout << b.print_count() << " " << b1.print_count() << endl; // prints '1 3'

    return 0;
}

ここでの問題は、参照A.s(を指すB_base::s)が1つのインスタンスから別のインスタンスにコピーされる一方で、別のインスタンスを指すように変更する必要があることB_base::sです。前のインスタンスがスコープ外になり、参照がぶら下がっている場合は、事態はさらに悪化する可能性があります。

私の質問は、メンバーベースのイディオムを使用してクラスの正しいコピーを作成するにはどうすればよいですか?(moveコンストラクターはcopy oneに似ていると思いますよね?)

4

1 に答える 1

0

この場合に実行している base-from-member イディオムは、次のことを意味します: with のclass B派生元を(with is: )Aのメンバーを使用して初期化する必要があります。 Bstring s

B b(10);
B b1(b); //   B(const B &other) : B_base(other), A(other) { }
         //   now A::s in b1 is a ref to b.s
A &a = b;//   and a.s is a ref to b.s too.
a.s =  "FAIL"; // we modify b, and A::s in b1 to!

この問題は、コピー コンストラクターを作成することで解決できます。

B(const B &other) : B_base(other), A(B_base::s) { }

また、 と 同じ名前を持つA::sB_base::s、物事が理解しにくくなります。

編集: クラス デザイナーとして、コピー コンストラクターの正確な意味を決定する必要があります。たとえば、この場合、新しく作成された各オブジェクトの印刷回数を
( で) 追跡したい場合があります。私が提案したコピーコンストラクターはそれを行います。A::cA

しかし、元の文字列のすべての印刷を追跡したい場合は、より複雑になります。古いA::cものを新しいAものにコピーすると、正しい初期化が行われることに注意してくださいA。これが問題にならない場合は、コンストラクターを変更できます。

A(string &s, int _c=0) : s(s), c(_c) { };

...

B(const B &other) : B_base(other), A(B_base::s, other.c) { }
于 2013-02-11T20:23:30.150 に答える