在派生-classes 中更改 return 类型的 base-class 方法

Change return type of base-class's method in its derived-classes

我知道如果修改后的 return 值与基础 class 中的值不共变,这是不可能的。不过,我还是想实现这样的东西!

对于我的特定问题,我需要某种名为 Node 的对象,其 范围从 intdoublechar 或其他什么,然后我需要为这些 Node 构建一个容器(std::vector),它可以灵活地接受任何类型的 Node.

我构建 Node 本质上是一个空的 class(它在实际代码中有一些东西对于它的所有派生 classes 是通用的,但对这个 post) 并指定了继承自它的两个类型 NodeIntNodeDouble

然后我用矢量作为成员构建我的 class Container。现在我希望能够遍历我的 Container 中的所有元素并让它们 value returned ...我该怎么做?

这是我的第一次尝试:

#include <iostream>
#include <vector>
#include <memory>

class Node{

};

class NodeChar : public Node{

    public:
        NodeChar(){ value = 'a'; }
        double getValue(){ return value; }

    private:
        double value;

};

class NodeInt : public Node{

    public:
        NodeInt(){ value = 5; }
        int getValue(){ return value; }

    private:
        int value;

};

class Collection{

  public:
    Collection( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    void setNodes( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    std::vector< std::shared_ptr<Node> > getNodes(){ return nodeCollection; }
  private:
    std::vector< std::shared_ptr<Node> > nodeCollection;    

};


int main() {

    NodeInt n1;
    NodeChar n2;

    std::vector< std::shared_ptr<Node> > vec (2) ;
    vec[0] = std::make_shared<Node>(n1);
    vec[1] = std::make_shared<Node>(n2);

    Collection C(vec);

    std::cout << (C.getNodes())[0]->getValue() << "  ---  " << 
        (C.getNodes())[1]->getValue() << std::endl;

    return 0;
}

但是当然 gcc 会因为 ‘class Node’ has no member named ‘getValue’ 而抱怨这种方式。我需要至少有一个 virtual 方法,这样我的程序就知道每个 Node 都有自己的 getValue.

尽管我需要 return 一些东西 ,但我一去尝试指定它的值......那是个问题!

我用两种方法解决了这个问题,这两种方法看起来都不必要地复杂和丑陋...

一个

使用通用指针 void* 作为 return 值,然后在第二时刻正确转换它们

#include <iostream>
#include <vector>
#include <memory>

class Node{

    public:
        virtual void* getValue() = 0 ;

};

class NodeChar : public Node{

    public:
        NodeChar(){ value = 'a'; }
        NodeChar( char v ){ value = v; }

        void* getValue(){ return &value; }

    private:
        char value;

};

class NodeInt : public Node{

    public:
        NodeInt(){ value = 5; }
        NodeInt( int v ){ value = v; }
        void* getValue(){ return &value; }

    private:
        int value;

};

class Collection{

  public:
    Collection( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    void setNodes( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    std::vector< std::shared_ptr<Node> > getNodes(){ return nodeCollection; }
  private:
    std::vector< std::shared_ptr<Node> > nodeCollection;    

};


int main() {

    NodeInt n1;
    NodeChar n2;

    std::vector< std::shared_ptr<Node> > vec (2) ;
    vec[0] = std::make_shared<NodeInt>(n1);
    vec[1] = std::make_shared<NodeChar>(n2);

    Collection C(vec);

    int* jnk1 = static_cast<int*>((C.getNodes())[0]->getValue());
    char* jnk2 = static_cast<char*>((C.getNodes())[1]->getValue());

    std::cout << *jnk1 << "  ---  " << *jnk2 << std::endl;

    // can't seem to make it work with shared pointers rather than standard int* or double* for the conversion from void*

    return 0;
}

两个

本质上相同,但 returning void(所以没什么),但需要在方法中更新通用指针 void* 作为参数。这需要再次将其投回......

#include <iostream>
#include <vector>
#include <memory>

class Node{

    public:
        virtual void getValue( void *& ptr ){}

};

class NodeChar : public Node{

    public:
        NodeChar(){ value = 'a'; }
        NodeChar( double v ){ value = v; }

        void getValue( void *& ptr ){ ptr = &value; }

    private:
        char value;

};

class NodeInt : public Node{

    public:
        NodeInt(){ value = 5; }
        NodeInt( int v ){ value = v; }
        void getValue( void *& ptr ){ ptr = &value; }

    private:
        int value;

};

class Collection{

  public:
    Collection( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    void setNodes( std::vector< std::shared_ptr<Node> > vec ){ nodeCollection = vec ; }
    std::vector< std::shared_ptr<Node> > getNodes(){ return nodeCollection; }
  private:
    std::vector< std::shared_ptr<Node> > nodeCollection;    

};


int main() {

    NodeInt n1;
    NodeChar n2;

    std::vector< std::shared_ptr<Node> > vec (2) ;
    vec[0] = std::make_shared<NodeInt>(n1);
    vec[1] = std::make_shared<NodeChar>(n2);

    Collection C(vec);

    void* jnk1 ; (C.getNodes())[0]->getValue(jnk1); 
    int* n1ptr = static_cast<int*>(jnk1);
    void* jnk2 ; (C.getNodes())[1]->getValue(jnk2); 
    char* n2ptr = static_cast<char*>(jnk2);

    std::cout << *(n1ptr) << "  ---  " << *(n2ptr) << std::endl;

    return 0;
}

是(是?)这是让它发挥作用的唯一方法吗?有没有你能想到的更好的解决方案或任何其他方法来实现相同的事情(即基础 class 中的虚拟方法具有不同的非协变 return 值,具体取决于派生的 classes)?

编辑: 正如评论中提到的,另一种方法可能是使用模板,但是如果我模板化 Node,那么当我去构建一个容器时,我只需要 Node 并且我不能混合使用不同的专用 classes,因此(如果我没有遗漏任何东西)这不能解决我的问题!

您还可以使用 boost::any,一种围绕任何类型的类型安全通用包装器。访问值时,您必须知道(或能够猜测)您拥有什么样的值,但它允许您 return 任何类型的值。

另一种可能性是使用访问者模式。以下是我找到的一些链接:

本质上,访问者模式允许将代码转发给另一个 class,它将执行适当的功能。

因此,在您的情况下,您或多或少可以有一个 class BaseVisitor,例如您可以从中派生出 class CoutVisitor。然后你将有 visit 函数接受每个所需的类型。

例如:

void CoutVisitor::Visit(const NodeChar &item)
{
    std::cout << item.getValue();
}

另一种可能性是使用 getCharValuegetDoubleValue,getValueType` 等函数并进行适当的转换或抛出异常。

当操作复杂且classes(NodeCharNodeInt...)相对稳定时,访问者模式可能更合适。

多个函数可能最适合 XML 或 JSON 数据表示...