2016-03-01 53 views
-1

我想将子对象存储在其父类型的容器中,然后根据容器中的子类型调用函数重载。那可能吗?基于父容器中的子项的调用超载函数

#include <vector> 

class Parent { public: }; 

class A : public Parent { public: }; 
class B : public Parent { public: }; 
class C : public Parent { public: }; 

class Hander 
{ 
public: 
    static void handle(A & a) {} 
    static void handle(B & b) {} 
    static void handle(C & c) {} 
}; 

int main() 
{ 
    A test1; 
    Hander::handle(test1); // compiles and calls the correct overload 

    Parent test2 = A(); 
    Hander::handle(test2); // doesn't compile 

    Parent * test3 = new A(); 
    Hander::handle(*test3); // doesn't compile 

    Parent children1[] = { A(), B(), C() }; 
    for (int i = 0; i < 3; ++i) 
     Hander::handle(children1[i]); // doesn't compile 

    std::vector<Parent*> children2 = { new A(), new B(), new C() }; 

    for (int i = 0; i < 3; ++i) 
     Hander::handle(*children2[i]); // doesn't compile 
} 
+4

阅读关于多态性和对象切片。 – NathanOliver

+2

**第一:**我认为'Parent test2 = A()'不会做你的想法。 * C++ *多态只存在于指针和引用。 'test2'具有'Parent'类型,由'A'构造,但不是'A'类型。 **第二:**从编译器中选择重载函数。如果你想调用正确的功能,你必须手动检查它是哪种类型。但是你可能对[* virtual functions *]感兴趣(http://stackoverflow.com/q/2391679/4967497)。使用虚拟功能,您可以使'handle'成为'A','B'和'C'的成员,并且它会始终自动调用正确的函数。 – JojOatXGME

+0

@JojOatXGME我知道能够手动检查类型(使用智能指针投射),但我的印象是反模式。它是否正确? –

回答

1

不,这是不可能的。

被调用的函数选择在编译时间。比方说,你有这样的代码:

Base &o = getSomeObject(); 
handle(o); 

编译器不知道真正 O型。它只知道它是BaseBase本身的某种亚型。这意味着它将搜索一个函数,用于加载Base类型的对象。

你可以自己实现该类型的支票或使用地图存储可能的功能:

Base &o = getSomeObject(); 
functionMap[typeid(o)](o); 

typeid如果Base多态型不会只工作这whay。这意味着它必须至少有一个虚拟功能。这将我们带到下一节:

但是你可以使用虚函数。

Virtual functions非静态可以覆盖的类的成员函数。正确的功能在运行时解决。下面的代码将输出的Subt代替Base

class Base { 
public: virtual std::string f() {return "Base"} 
}; 
class Subt : public Base { 
public: virtual std::string f() {return "Subt"} 
}; 

int main() { 
    Subt s; 
    Base &b = s; 
    std::cout << b.f() << std::endl; 
} 

您可以在Subt定义忽略virtual。函数f()已被定义为虚拟在它的基类中。

类与至少一个虚拟函数(也称为多晶型类型)被存储到虚拟函数表一个参考(也称为虚表)。该表用于在运行时获得正确的功能。

在你的问题的问题就可以解决这样的:

class Parent { 
public: 
    virtual void handle() = 0; 
}; 

class A : public Parent { 
public: 
    void handle() override { /* do something for instances of A */ } 
}; 
class B : public Parent { 
public: 
    void handle() override { /* do something for instances of B */ } 
}; 
class C : public Parent { 
public: 
    void handle() override { /* do something for instances of C */ } 
}; 

int main() 
{ 
    std::vector<std::unique_ptr<Parent>> children = { 
      std::make_unique<A>(), 
      std::make_unique<B>(), 
      std::make_unique<C>()}; 

    for (const auto &child : children) 
     child->handle(); 
} 

注意有关兼容性:关键字autooverride仅提供C++ 11以上。从C++ 11开始,range-based for loopstd::unique_ptr也是可用的。函数std::make_unique自C++ 14起可用。但是虚拟功能也可以与旧版本一起使用。

另一个提示:

多态性也只有引用和指针工作。以下称之为Base::f()而不是Subt::f()

Subt s; 
Base b = s; 
std::cout << b.f() << std::endl; 

在这个例子中b将只包含Base类型,而不是Subt的对象。该对象仅在Base b = s;处创建。它可能会从s复制一些信息,但它不再是s。这是Base类型的新对象。

+0

非常感谢您的深入解答!这解释了我正在寻找的一切以及更多。是的,我总是使用托管指针,除非我为SO提供了一个简单的例子。 –