重写合格的虚拟方法

发布于 2024-10-28 18:35:33 字数 857 浏览 8 评论 0原文

我和多个家长一起上 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:成员声明中的非法限定名称”

在不同的情况下,我可能只是重命名这些方法,或者使它们成为纯虚拟的编译器建议。然而,类层次结构和许多外部问题使得第一个选项极其困难,而第二个选项则不可能。

有人有建议吗?为什么限定符只允许用于纯虚方法?有没有办法同时重写虚拟方法并解决歧义?

I have C++ class with multiple parents; each parent defines a function with a common name but a different purpose:

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

If that was it, I would have no problem - I could resolve the ambiguity it with a using statement, and I could choose which one to call using the base class names and the scope resolution operator.

Unfortunately, the derived class needs to override them both:

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

This doesn't work, not because it introduces a new ambiguity (although it may), but because

"error C3240: 'myFunc' : must be a non-overloaded abstract member function of 'BaseA'"

"error C2838: illegal qualified name in member declaration"

Under different circumstances I might just rename the methods, or make them pure virtual as the compiler suggests. However, the class hierarchy structure and a number of external issues make the first option extremely difficult, and the second impossible.

Does anyone have a suggestion? Why are qualifiers only allowed for pure virtual methods? Is there any way to simultaneously override virtual methods and resolve ambiguities?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

宛菡 2024-11-04 18:35:33

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() {}
};

Microsoft allows that syntax (it's available beginning in Visual C++ 2005). They also introduced a new, more powerful syntax for managed code only.

Neither one was included in C++0x.

See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2108.html


I think this is a workaround:

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() {}
};
不乱于心 2024-11-04 18:35:33

您可以使用组合对象。

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.
}

这并不是特别简洁,而且有些限制,但它确实在某些情况下有效。

You could use a composition object.

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.
}

This isn't especially neat and it's somewhat restricted, but it does work some circumstances.

迷雾森÷林ヴ 2024-11-04 18:35:33

这是多重继承的大问题之一。当您继承多个同名函数来确定应该覆盖哪个函数时,总会出现问题,请参阅 钻石问题。您不能覆盖两者,因为函数语法(函数名称和运算符)必须是唯一的。

This is one of the big issues with multiple inheritance. There are always issues when you have multiple functions of the same name being inherited to determine which one is supposed to be overridden see The Diamond Problem. You cannot override both as the function syntax (function name and operators) must be unique.

[旋木] 2024-11-04 18:35:33

我意识到这个问题很老了,但它有很多观点,如果您是界面的作者,有一个干净的方法可以解决这个问题。

许多人认为虚拟接口应该具有公共非虚拟函数,这些函数在内部遵循私有虚拟函数(我同意他们的观点)。这有一些优点,其中之一是非虚拟名称可以具有不同的含义,因为它们与接口的绑定更紧密:

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

这种方法的另一个优点是基类实现可以代表对象管理互斥体和哨兵之类的东西。派生函数,例如:

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;

I realise that this question is old, but it's had a lot of views and there is a clean way to solve this if you are the author of the interfaces.

Many people believe that virtual interfaces should have public non-virtual functions that defer to private virtual functions internally (I agree with them). This has a few advantages, one of them being that non-virtual names can have distinct meanings since they are more strongly bound to the interface:

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

Another advantage of this approach is that base class implementations can manage things like mutexes and sentinels on behalf of the derived functions, for example:

struct base {

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

private:
  virtual void handle_do() = 0;

  std::mutex _m;
};

Yet another advantage is that some useful free-function operators need only be defined once for the entire class hierarchy:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文