【问题标题】:Maintain reference to any object type in C++?维护对 C++ 中任何对象类型的引用?
【发布时间】:2010-12-10 14:34:58
【问题描述】:

我正在尝试自学 C++,而我一直使用的传统“新语言”练习之一是实现一些数据结构,例如二叉树或链表。在 Java 中,这相对简单:我可以定义一些类 Node 来维护一个实例变量 Object data,以便有人可以在列表或树的每个节点中存储任何类型的对象。 (后来我使用泛型对其进行了修改;这不是这个问题的意义所在。)

我找不到类似的、惯用的 C++ 方式来存储“任何类型的对象”。在 C 语言中,我会使用 void 指针;显然,同样的事情适用于 C++,但是当我构造std::string 的实例并尝试将其存储到列表/树中时遇到问题(关于从std::string&void* 的无效转换)。有没有这样的方法? C++ 是否有相当于 Java 的 Object(或 Objective-C 的 NSObject)?

额外问题:如果没有,并且我需要继续使用 void 指针,那么将 std::string 存储到 void* 中的“正确”方法是什么?我偶然发现了static_cast<char*>(str.c_str()),但这对于我正在尝试做的事情来说似乎有点冗长。有没有更好的办法?

【问题讨论】:

  • 为什么这个问题被否决了?
  • 我也很想知道 - 我找不到骗子或任何东西......
  • 这就像一个老笑话。病人:我这样做的时候很痛。医生:不要那样做。在 C++ 中,不要做“任何事情”的集合。
  • @Jerry Coffin- +1,但需要注意的是,学习之后一切都会发生。在 C++ 中实现一个集合来学习语言结构并没有错,但是当你完成学习后就把它扔掉。

标签: c++ pointers object linked-list


【解决方案1】:

模板是执行此操作的静态方式。它们的行为类似于 Java 和 C# 泛型,但 100% 是静态的(编译时)。如果您不需要在同一个容器中存储不同类型的对象,请使用这个(其他答案很好地描述了这个)。

但是,如果您需要在同一个容器中存储不同类型的对象,则可以通过将指针存储在基类上 以动态方式进行。当然,您必须定义自己的对象层次结构,因为 C++ 中没有这样的“对象”类:

#include <list>

class Animal {
public:
   virtual ~Animal() {}
};

class Dog : public Animal {
public:
   virtual ~Dog() {}
};

class Cat : public Animal {
public:
   virtual ~Cat() {}
};

int main() {
    std::list<Animal*> l;
    l.push_back(new Dog);
    l.push_back(new Cat);

    for (std::list<Animal*>::iterator i = l.begin(); i!= l.end(); ++i)
        delete *i;

    l.clear();

    return 0;
}

智能指针更易于使用。以boost::smart_ptr 为例:

std::list< boost::smart_ptr<Animal> > List;
List.push_back(boost::smart_ptr<Animal>(new Dog));
List.push_back(boost::smart_ptr<Animal>(new Cat));
List.clear(); // automatically call delete on each stored pointer

【讨论】:

    【解决方案2】:
    static_cast<char*>(str.c_str())
    

    我觉得很奇怪。 str.c_str() 检索类似 C 的字符串,但类型为 const char *,要转换为 char *,您通常会使用 const_cast&lt;char *&gt;(str.c_str())。除了这样做不好,因为你会干涉string 的内部。你确定你没有收到警告吗?

    您应该可以使用static_cast&lt;void *&gt;(&amp;str)。您收到的错误消息向我表明您还有其他问题,因此如果您可以发布代码,我们可以查看它。 (数据类型std::string&amp; 是对string 的引用,而不是指向其中的指针,因此错误消息是正确的。我不知道您是如何获得引用而不是指针的。)

    而且,是的,这很冗长。它的目的是。在 C++ 程序中,转换通常被认为是一种难闻的气味,Stroustrup 希望转换容易找到。正如在其他答案中所讨论的,构建任意基类型的数据结构的正确方法是使用模板,而不是强制转换和指针。

    【讨论】:

      【解决方案3】:

      你可以看看 boost::any 类。它是类型安全的,你可以把它放到标准集合中,不需要链接任何库,类在头文件中实现。

      它允许你编写这样的代码:

      #include <list>
      #include <boost/any.hpp>
      
      typedef std::list<boost::any> collection_type;
      
      void foo()
      {
          collection_type coll;
          coll.push_back(boost::any(10));
          coll.push_back(boost::any("test"));
          coll.push_back(boost::any(1.1));
      }
      

      完整文档在这里:http://www.boost.org/doc/libs/1_40_0/doc/html/any.html

      【讨论】:

        【解决方案4】:

        与 Java 不同,C++ 没有所有对象都继承自的基础对象。您想要做的通常方法是使用templates。标准 C++ 库中的所有容器都使用这种方法。

        与 Java 不同,C++ 不依赖多态/继承来实现通用容器。在 Java 中,所有对象都继承自 Object,因此任何类都可以插入到采用 Object 的容器中。然而,C++ 模板是编译时构造,它指示编译器为您使用的每种类型实际生成不同的类。因此,例如,如果您有:

        template <typename T>
        class MyContainer { ... };
        

        然后您可以创建一个采用std::string 对象的MyContainer,以及另一个采用ints 的MyContainer。

        MyContainer<std::string> stringContainer;
        stringContainer.insert("Blah");
        
        MyContainer<int> intContainer;
        intContainer.insert(3342);
        

        【讨论】:

        • 并非如此。它们是完全不同的,即使语法表面上是相同的。
        • 详细来说,Java 泛型是一种依赖于继承和多态的运行时机制。 C++ 模板是编译时构造,它指示编译器为每个参数化类型生成代码。有关更多信息,请参阅我编辑的答案。
        • 事实上,不管你信不信,C++ 模板实际上与 C 宏的共同点多于 Java 泛型。当然,它们比宏更安全、更强大。
        • @Charles:这就像是说 X 博士与 Mangler 博士的共同点多于 Y 博士。这种比较既没有帮助也没有误导性。
        • @Martin,我明白你的意思,但是模板经常被比作 Java 泛型,(由于语法相似,表面上,功能相似),而实际上它们是巨大的不同的。 (编译时代码生成与运行时多态性。)我认为与 C 宏(也是编译时代码生成机制)的比较有助于突出这一点。但你是对的,模板与宏也有很大不同,宏只不过是一种查找/替换预处理机制。
        【解决方案5】:

        您应该能够使用标准 C 样式转换将 void* 转换为 string*。请记住,引用在使用时不会被视为指针,而是被视为普通对象。因此,如果您通过引用一个函数来传递一个值,您仍然必须取消对它的引用才能获得它的地址。

        但是,正如其他人所说,更好的方法是使用模板

        【讨论】:

        • 在 C++ 中不鼓励使用 C 风格的强制转换。
        【解决方案6】:

        您正在寻找的是模板。它们允许您创建允许您采用任何数据类型的类和函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-26
          • 1970-01-01
          • 1970-01-01
          • 2016-07-02
          • 1970-01-01
          相关资源
          最近更新 更多