2

次の問題の解決策をいただければ幸いです。2種類の変数があります

class Type1Str : public string
{
public:
  Type1Str(const string & str) : string(str) {}
};

class Type2Str : public string
{
public:
  Type2Str(const string & str) : string(str) {}
};

文字列のすべての利点を享受したいのですが、相互割り当てを防ぎます。

Type1Str t1; 
Type2Str t2;
t1 = t2;                           // <= should not be allowed
Type2Str t2_1(t1);                 // <= should not be allowed
Type2Str t2_2("a string");         // <= should be allowed

これどうやってするの?

4

3 に答える 3

3

これでうまくいくはずです:

#include <string>

using namespace std;

class Type2Str;

class Type1Str : public string
{
public:
    Type1Str() {}
    Type1Str(const std::string & str) : std::string(str) {}

private:
    Type1Str(const Type2Str &);  // deliberately private and unimplemented
    Type1Str & operator = (const Type2Str &);  // deliberately private and unimplemented
};

class Type2Str : public string
{
public:
    Type2Str() {}
    Type2Str(const std::string & str) : std::string(str) {}

private:
    Type2Str(const Type1Str &);  // deliberately private and unimplemented
    Type2Str & operator = (const Type1Str &);  // deliberately private and unimplemented
};

int main(int, char **)
{
   Type1Str t1;
   Type2Str t2;
   t1 = t2;                           // <= should not be allowed
   Type2Str t1_2(t1);                 // <= should not be allowed
   Type2Str t2_2("a string");         // <= should be allowed
   return 0;
}
于 2012-06-03T06:10:40.457 に答える
2

コンストラクターを宣言のプライベート/保護された部分に配置するだけです。

class Type1Str : public string
{
    Type1Str(const class Type2Str&); // use forward declaration for class Type2Str
public:
    Type1Str(const string & str) : string(str) {}
};

class Type2Str : public string
{
    Type2Str(const Type1Str&); // this does not allow 'Type2Str t2_2(t1);'
public:
    Type2Str(const string & str) : string(str) {}
};
于 2012-06-03T06:09:41.073 に答える
2

残念ながら、OP はコンパイルされなかったコードを提示しました。

以下は、コンパイルできるように最も自然な方法で修正された元のコードです。

class Type1Str : public string
{
public:
    Type1Str() {}
    Type1Str(const string & str) : string(str) {}
};

class Type2Str : public string
{
public:
    Type2Str() {}
    Type2Str(const string & str) : string(str) {}
};

上記が意図されたものであると仮定すると、相互代入を防ぐために必要なのは、変換コンストラクターを作成することだけexplicitです。

#include <string>
using std::string;

class Type1Str : public string
{
public:
    Type1Str() {}
    explicit Type1Str(const string & str) : string(str) {}
};

class Type2Str : public string
{
public:
    Type2Str() {}
    explicit Type2Str(const string & str) : string(str) {}
};

int main()
{
    Type1Str t1; 
    Type2Str t2;

    t1 = t2;            // A. !Invalid, involves an implicit conversion.
    Type2Str t2_2(t1);  // B. Still allowed, here the conversion is explicit.
}

B とマークされた行で、呼び出される変換は、実引数の暗黙的な変換ではなく、Type2Strの明示的な変換コンストラクターです。が であるため、実引数t1はそのコンストラクタの仮引数と直接一致しt2 ますstd::string。OPはラインBも防ぎたい.

簡単な方法の 1 つは、変換をさらに明確にすること、つまり名前を付けることです。

つまり、正式な引数にキャリア型を導入するか、追加のダミーの変換名引数を導入するか、パブリック変換コンストラクターをパブリック ファクトリ関数に置き換えるかのいずれかです。ファクトリ関数は最も単純で、2012 年の時点でコストがかかりませんが、メンテナンスにコストがかかります。その機能を提供するには、派生クラスで再実装する必要があります。ここで言及されている他の 2 つの解決策のうち、追加の仮名引数は、実装が最も簡単で、コードも最小です。

#include <string>
using std::string;

namespace from { enum StdString { stdString }; };

class Type1Str : public string
{
public:
    Type1Str() {}
    Type1Str( from::StdString, const string & str) : string(str) {}
};

class Type2Str : public string
{
public:
    Type2Str() {}
    Type2Str( from::StdString, const string & str) : string(str) {}
};

int main()
{
    Type1Str t1; 
    Type2Str t2;
    Type2Str t3( from::stdString, t1 ); // OK.

    t1 = t2;            // A. !Invalid, no conversion possible.
    Type2Str t2_2(t1);  // B. !Invalid, no conversion possible.
}

それでは、代わりに、直接コピーを禁止したいすべての型に対してプライベート コンストラクターと代入演算子を宣言することの何が問題なのですか?

まあ、脆弱であることに加えて、O( n 2 ) 宣言に相当するn型の場合です。したがって、一般的には良い考えではありません。しかし、私がこれを書いているとき、OPはそのアプローチを「解決策」として選択しました。ただ注意してください:一般的に、これは良い考えではありません

于 2012-06-03T07:02:50.473 に答える