6

複数の親を持つC++クラスがあります。各親は、共通の名前で異なる目的で関数を定義します。

class BaseA
{
    virtual void myFunc();  // does some task
};
class BaseB
{
    virtual void myFunc();  // does some other task
};
class Derived : public BaseA, public BaseB;

そうであれば、問題はありません。usingステートメントを使用してあいまいさを解決し、基本クラス名とスコープ解決演算子を使用してどちらを呼び出すかを選択できます。

残念ながら、派生クラスはそれらの両方をオーバーライドする必要があります。

class Derived : public BaseA, public BaseB
{
    virtual void BaseA::myFunc(); // Derived needs to change the way both tasks are done
    virtual void BaseB::myFunc();
}

これは機能しません。これは、新しいあいまいさを導入するためではなく(可能性はありますが)、

"エラーC3240:'myFunc':'BaseA'のオーバーロードされていない抽象メンバー関数である必要があります"

「エラーC2838:メンバー宣言に不正な修飾名があります」

さまざまな状況下で、メソッドの名前を変更するか、コンパイラーが提案するようにメソッドを純粋な仮想にすることができます。ただし、クラス階層構造と多くの外部の問題により、最初のオプションは非常に難しく、2番目のオプションは不可能です。

誰か提案がありますか?修飾子が純粋仮想メソッドでのみ許可されるのはなぜですか?仮想メソッドをオーバーライドし、あいまいさを解決する方法はありますか?

4

4 に答える 4

3

Microsoftは、その構文を許可しています(Visual C ++ 2005以降で使用可能です)。 また、マネージコード専用の新しいより強力な構文も導入しました

どちらもC++0xには含まれていませんでした。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2108.htmlを参照してください


これは回避策だと思います。

class BaseA
{
protected:
    virtual void myFunc();  // does some task
};
class BaseB
{
protected:
    virtual void myFunc();  // does some other task
};
class ShimA : virtual BaseA
{
    virtual void myFunc() { myFuncA(); }
protected:
    virtual void myFuncA() { BaseA::myFunc(); }
};
class ShimB : virtual BaseB
{
    virtual void myFunc() { myFuncB(); }
protected:
    virtual void myFuncB() { BaseB::myFunc(); }
};
class Derived : public virtual BaseA, public virtual BaseB, protected ShimA, protected ShimB
{
     virtual void myFuncA() {}
     virtual void myFuncB() {}
};
于 2011-03-30T03:18:54.827 に答える
2

コンポジションオブジェクトを使用できます。

class Derived : public BaseB {        
    struct temp : public BaseA {
        virtual void myFunc() {
             d->BaseAMyFunc();
        }
        Derived* d;
    };
    temp t;
public:
    Derived() {
        t.d = this;
    }
    operator BaseA&() { return temp; }
    operator const BaseA&() const { return temp; }
    void myFunc(); // refers to BaseB::myFunc()
    void BaseAMyFunc(); // called when BaseA::myFunc() is called.
}

これは特にきちんとしたものではなく、多少制限されていますが、状況によっては機能します。

于 2011-03-30T03:04:02.590 に答える
1

これは、多重継承に関する大きな問題の1つです。同じ名前の複数の関数を継承して、どの関数をオーバーライドするかを決定する場合、常に問題が発生します。 「菱形継承問題」を参照してください。関数の構文(関数名と演算子)は一意である必要があるため、両方をオーバーライドすることはできません。

于 2011-03-30T03:03:21.887 に答える
0

この質問は古いものだと思いますが、多くの見解があり、インターフェースの作成者であれば、これを解決するためのクリーンな方法があります。

多くの人々は、仮想インターフェイスには、内部でプライベート仮想関数を延期するパブリック非仮想関数が必要であると考えています(私はそれらに同意します)。これにはいくつかの利点があります。そのうちの1つは、非仮想名がインターフェイスにより強くバインドされているため、明確な意味を持つことができることです。

struct BaseA
{
  virtual ~BaseA() = default;

  void func() 
  {
    handle_a_func();
  }

private:
  virtual void handle_a_func() = 0;
};

struct BaseB 
{
  virtual ~BaseB() = default;

  int func() const  // note the different signature and return type
  {
    handle_b_func();
  }

private:
  virtual int handle_b_func() const = 0;
};

// now provide an implementation

struct ABImpl : public BaseA, public BaseB
{
  ABImpl() {}

private:
  void handle_a_func() override final 
  {
    // alter some state
  }

  int handle_b_func() const override final
  {
    return _x;
  }

  int _x = 0;
};        

// now use the class
auto ab = make_shared<ABImpl>();

auto a = static_pointer_cast<BaseA>(ab);
auto b = static_pointer_cast<const BaseB>(ab);

a->func();  // uses A's mutable interface
auto x = b->func();  // uses B's const interface

このアプローチのもう1つの利点は、基本クラスの実装が、派生関数に代わってミューテックスやセンチネルなどを管理できることです。たとえば、次のようになります。

struct base {

  void do() {
    std::unique_lock<std::mutex> l(_m);
    handle_do();
  }

private:
  virtual void handle_do() = 0;

  std::mutex _m;
};

さらに別の利点は、いくつかの有用な自由関数演算子をクラス階層全体に対して一度だけ定義する必要があることです。

struct base
{
  void write_to(std::ostream& os) const {
    // lock a mutex here?
    handle_write(os);
  private:
    virtual void handle_write(std::ostream& os) const {
      os << "write base class info here\n";
    }
};

inline std::ostream& operator<<(std::ostream& os, const base& b) {
  b.write_to(os);
  return os;
}

struct derived : base {
private:
  virtual void handle_write(std::ostream& os) const override {
    base::handle_write(os);  // handle base class implementation
    std::cout << "write relevant data here. We could still be overridden safely\n";
  }
};

// write any derived class like this:
auto b = unique_ptr<base> { new derived() };
cout << "here is a kind-of b:\n" << *b << endl;
于 2014-09-16T10:35:08.847 に答える