【问题标题】:c++ overriding a function only for a specific instancec ++仅针对特定实例覆盖函数
【发布时间】:2013-08-14 00:05:29
【问题描述】:

我想知道是否有办法只为特定实例覆盖函数。例如,

class A
{
public:
    ...
    void update();
    ...
}

int main()
{
    ...
    A *first_instance = new A();
    // I want this to have a specific update() function.
    // ex. void update() { functionA(); functionB(); ... }

    A *second_instance = new A();
    // I want this to have a different update() function than the above one.
    // ex. void update() { functionZ(); functionY(); ...}

    A *third_instance = new A();
    // ....so on.
    ...
}

有没有办法做到这一点?

【问题讨论】:

  • 为什么不能直接将update()定义为virtual,然后定义一个覆盖它的子类?
  • 当实例应该是不同类型时,为什么要根据实例进行区分?
  • 我打算为每个子类定义子类,但我想知道是否还有其他方法。
  • 这被称为 XY 问题。你想做什么?
  • Scala 和其他一些富语言的对象可以有自己的方法...... C++ 没有。

标签: c++ oop


【解决方案1】:

我觉得虚函数正是你想要的,有了虚函数,同类型的不同实例可以有不同的功能,但是你需要继承基类。例如

class A
{
    public:
        ...
        virtual void update()
        {
            std::cout << "Class A\n";
        }
        ...
};

class B: public A
{
    public:
        virtual void update()
        {
            std::cout << "Class B\n";
        }
};

class C: public A
{
    public:
        virtual void update()
        {
            std::cout << "Class C\n";
        }            

};

int main()
{
    ...
    A *first_instance = new A();
    // I want this to have a specific update() function.
    // ex. void update() { functionA(); functionB(); ... }

    A *second_instance = new B();
    // I want this to have a different update() function than the above one.
    // ex. void update() { functionZ(); functionY(); ...}

    A *third_instance = new C();
    // ....so on.
    ...
}

以上代码中的每个实例都会绑定不同的更新函数。

另外,你也可以使用函数指针来实现你的需求,但不推荐。例如

class A
{
    public:
        A(void(*u)())
        {
            this->update = u;
        }
        ...
        void (*update)();
};

void a_update()
{
    std::cout << "update A\n";
}

void b_update()
{
    std::cout << "update B\n";
}

void c_update()
{
    std::cout << "update C\n";
}

int main()
{
    ...
    A first_instance(a_update);
    // I want this to have a specific update() function.
    // ex. void update() { functionA(); functionB(); ... }

    A second_instance(b_update);
    // I want this to have a different update() function than the above one.
    // ex. void update() { functionZ(); functionY(); ...}

    A third_instance(c_update);
    // ....so on.
    ...
}

希望有所帮助!

【讨论】:

  • 谢谢你的回答..我实际上打算首先这样做,但我不喜欢仅仅为不同的 update() 函数创建大约 50 个子类......我估计没有别的办法了。
  • @ChaewonLee “我想没有别的办法了”:几乎可以肯定,如果你能解释一下你想做什么。
  • @ChaewonLee ,我刚刚更新了我的答案,还有另一种方法可以实现您的要求,即函数指针,请查看,;)
  • 50 节课 - 你在开玩笑。由于您不具体说明您要达到的目标,因此不可能给出一个好的解决方案。您听说过地图/列表/变量等吗?
  • @Hanfeng 函数指针有什么问题(“……不推荐……”)??
【解决方案2】:

在课堂上保持function

#include <iostream>
#include <functional>

using namespace std;

class Foo
{
public:
    Foo(const function<void ()>& f) : func(f)
    {
    }

    void callFunc()
    {
        func();
    }
private:
    function<void ()> func;
};

void printFoo() { cout<<"foo"<<endl; }
void printBar() { cout<<"bar"<<endl; }

int main()
{
    Foo a(printFoo);
    Foo b(printBar);
    a.callFunc();
    b.callFunc();
}

【讨论】:

  • 请注意,这需要 C++11。对于 C++03,boost 提供了function 的实现。
  • Something 应该保存在类实例中,但如果没有来自 OP 的更多详细信息,我们无法知道需要什么。
【解决方案3】:

您可能已经注意到,类的结尾大括号通常后跟分号,而函数的结尾大括号 while 循环等则没有。这是有原因的,这与 C 中 struct 的一个特性有关。因为 classstruct 几乎相同,所以这个特性也存在于 C++ 类中。

基本上,C 中的 struct 可以声明一个命名实例,而不是(或以及)一个命名的“类型”(因为 C 中的 struct 类型本身不是有效的类型名称,所以使用引号) .因此,C++ 类可以做同样的事情,尽管 AFAIK 可能对该类可以做的其他事情有严格的限制。

目前我无法检查,而且我记得肯定不是我使用过的东西,但这可能意味着您可以声明一个从基类继承的命名类实例而不给它一个类名。仍然会有派生类型,但会是匿名的。

如果有效,它应该看起来像...

class : public baseclass  //  note - no derived class name
{
  public:
    virtual funcname ()
    {
      ...
    }
} instancename;

就个人而言,即使这是有效的,出于多种原因,我也会避免使用它。例如,缺少类名意味着无法单独定义成员函数。这意味着整个类的声明和定义必须放在你希望声明实例的地方——在函数中间甚至是全局变量列表中都会出现很多混乱。

如果没有类名,可能就无法声明构造函数或析构函数。如果您有来自基类的非默认构造函数,AFAIK 就无法使用 this 指定构造函数参数。

正如我所说,我没有检查过这个 - 语法很可能是非法的和丑陋的。

一些更实用的方法来改变每个实例的行为包括......

  1. 使用依赖注入 - 例如为行为的某些部分提供函数指针或类实例(或 lambda)作为构造函数参数。

  2. 使用模板类 - 有效的编译时依赖注入,将依赖作为函数参数提供给模板。

【讨论】:

  • 您甚至可以在new 语句中使用它:new (class : public baseclass { public: virtual funcname () { ... } });
  • @JohnB - 这很有趣。而且有点吓人。
  • 显然,它是非标准的,而是 gcc 4.6.3 中的一个错误:stackoverflow.com/questions/18171684/anonymous-classes
【解决方案4】:

我认为最好能告诉我们为什么需要为特定实例重写函数。
但这是另一种方法:Strategy pattern.

您的班级需要一个代表某些行为的成员。因此,您正在创建一些抽象类,它将成为不同行为的接口,然后您将在该抽象类的子类中实现不同的行为。因此,您可以随时为任何对象选择这些行为。

class A;//forward declaration

class Updater
{
public:
    virtual ~Updater() {};//don't forget about virtual destructor, though it's not needed in this case of class containing only one function
    virtual void update(A&) = 0;
}

class SomeUpdater
{
public:
    virtual void update(A & a);//concrete realisation of an update() method
}

class A
{
private:
    Updater mUpdater;
public:
    explicit A(Updater updater);//constructor takes an updater, let's pretend we want to choose a behaviour once for a lifetime of an object - at creation
    void update()
    {
        mUpdater.update(this);
    }
}

【讨论】:

    【解决方案5】:

    您可以使用本地类,但就个人而言,我认为其他答案中提到的“类中的保留函数”方法更好。仅当doFunc 必须访问您的基类的内部时,我才会推荐以下方法,而这在成员变量中保存的函数中是不可能的:

    class ABase {
    public:
        void Func () { this->doFunc (); }
    private:
        virtual void doFunc () = 0;
    public:
        virtual ~ABase () { }
    };
    
    ABase* makeFirstA () {
        class MyA : public ABase {
            virtual void doFunc () { std::cout << "First A"; }
        };
        return new MyA;
    }
    
    ABase* makeSecondA () {
        class MyA : public ABase {
            virtual void doFunc () { std::cout << "Second A"; }
        };
        return new MyA;
    }
    
    int main () {
        std::shared_ptr<ABase> first (makeFirstA ());
        std::shared_ptr<ABase> second (makeSecondA ());
        first->Func ();
        second->Func ();
    }
    

    从设计模式的角度来看,“本地类”方法实现了模板方法模式,而“在成员变量中保存函数”方法反映了策略模式。哪个更合适取决于您需要实现的目标。

    【讨论】:

    • 一个保存在变量中的函数可以通过传递参​​数的简单方法访问你的类的内部......你可以通过传递this来访问整个shebang如果给予适当的访问权限。而且 C++11 提供了闭包,可以捕获(“关闭”)对任何所需内容的引用。
    • 这是通常的继承与组合决定。通过传递this,你可以让函数只访问类的公共接口,而不是受保护的接口。我看不出闭包在这里应该有什么帮助。
    • 有哪些启发式标准可以根据此代码的疑似设计目标来决定使用哪个?
    猜你喜欢
    • 2019-07-02
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    相关资源
    最近更新 更多