【问题标题】:How to pass a Class as Parameter如何将类作为参数传递
【发布时间】:2012-10-18 01:02:03
【问题描述】:

在 C++ 中有没有办法将类型(例如类)作为参数传递给函数?

解释为什么我需要这个:有一个抽象的data 类和一个manager 类。 manager 类包含data 的派生类的对象列表(或在本例中为映射)。如this question 的回答中所述,我为此使用unique_ptr

class Data{}; // abstract

class Manager
{
    map<string, unique_ptr<Data>> List;
};

现在假设我想向管理器添加一个新的data 存储。

class Position : public Data
{
    int X;
    int Y;
}

我如何告诉经理创建该类型的对象并将unique_ptr 引用给它?

Manager manager;
manager.Add("position data", Position);

在这种情况下,我需要将类 Position 传递给管理器类的 add 函数,因为我不想先创建实例然后将其发送给管理器。

然后,如何将该类的对象添加到List

我不确定在 C++ 中是否有办法做到这一点。如果这不能轻松完成,我真的很想看到一个解决方法。非常感谢!

【问题讨论】:

  • 您是否尝试过使用模板?
  • 没有,因为我不熟悉使用模板。但我对使用它们和更多地了解它们持开放态度。
  • 我认为下面的答案提供了很好的模板解决方案。

标签: c++ class function types parameters


【解决方案1】:

定义函数如下:

void Manager::Add(String title,Position pos){ /*snip*/ }

然后你可以使用如下函数:

Manager manager;
Position posData;
//setup the posData
manager.Add("position data",posData);

【讨论】:

  • 我做不到。由于经理必须能够存储从data 派生的所有类型,我需要的是一个通用的解决方案。此外,我不想在我的主函数中创建一个实例。
  • 您可以传递您插入到地图中的临时 Position 类。
  • 我可以,但如果有办法不这样做,我更愿意这样做。尽管如此,管理器必须能够添加数据派生类的所有可能对象,而不仅仅是位置。
  • 那么Position 可以直接更改为Data。但似乎你会从其他答案中找到你喜欢的答案。
【解决方案2】:

您可以使用模板。在从Data 派生的每种类型中,您必须定义一个“创建者”函数,其原型如下:Derived* create()。它将在内部调用(您也可以返回 unique_ptr,但这需要更多内存)。

例如:

struct Position: public Data
{
    // ...
    static Position* create()
    {
        return new Position();
    }
};

Add 方法将是:

template<typename D>
void Add(String str)
{
    List.insert(std::make_pair(str, std::unique_ptr<Data>(D::create())));
}

然后你这样使用它:

Manager manager;
manager.Add<Position>("position data");

编辑

您还可以使用Add 方法摆脱create 函数:

template<typename D>
void Add(String str)
{
    List.insert(std::make_pair(str, std::unique_ptr<Data>(new D())));
}

优点:数据结构代码少。

不方便:数据结构对其构建方式的控制较少。

【讨论】:

  • 这也是一个不错的解决方案。但是有没有办法摆脱 create 函数? (也许以某种方式将其移至基类?)
  • 可以,可以直接使用new D()。结构上的控制较少,但更容易实现。见编辑。
  • 看起来不错,但有编译器错误。 Binary Operator '=': There was no operator found which accepts an operand of type 'Position *'.
  • 是的,那是代码只是为了显示你应该做什么'自我记录代码)。编辑这个。
  • 感谢现在有效。 (顺便说一句,您在两行中都错过了一个)。)
【解决方案3】:

这样的事情怎么样:

class Manager
{
 public:
  template <typename T>
  void addData(const std::string& title)
  {
    List.insert(std::make_pair(title, std::unique_ptr<Data>(new T));
  }
 private:   
  map<string, unique_ptr<Data>> List;
};

然后

Manager manager;
manager.addData<Position>("position data");

【讨论】:

  • 这很好。有什么方法可以限制typename T 派生自Data
  • @sharethis 很好,如果T 不是从Data 派生的,std::unique_ptr&lt;Data&gt;(new T) 会产生编译问题,除非从T 转换为Data
  • @sharethis 如果你真的想确定,并且你有 C++11 支持,你可以使用 std::is_base_ofstatic_assert。 Boost 提供了 c++03 的替代方案。
  • 哦,你能提供一个简短的例子来说明你最后的想法吗?
  • @sharethis 类似于static_assert(std::is_base_of&lt;Data, T&gt;::value, "T is not derived from Data"); 但实际上,除非TData 的基础,否则我想不出上面的代码可以编译的情况。
【解决方案4】:

使用unique_ptr 意味着致力于使用动态分配的对象。 (不完全正确:您可以提供一个不调用 delete 的自定义删除器,但这会成为类型的一部分;这意味着您将致力于使用 动态分配的对象。 )

void Manager::Add(String title, unique_ptr<Data> d) {
    List[title] = d;
}

称它为:

Manager manager;
unique_ptr<Data> pos(new Position);
// Set it up
manager.Add("position data", pos);

注意:如果您希望能够对数据对象进行任何操作,您可能希望在class Data 中至少声明一个虚函数。

【讨论】:

  • 谢谢。也许unique_ptr 不适合我。因为我想提供一个 manager.Get(string Name); 返回指向该数据对象的指针。你怎么看?
  • 哎呀,我忘了List 实际上是map :-P 我已经编辑了Add() 实现来解决这个问题。 unique_ptr 的行为就像一个指针,所以你仍然可以写unique_ptr&lt;Data&gt; Manager::Get(String title) { return List[title]; },就像你写一个普通的指针一样。 unique_ptr 所做的就是确保自动清理动态分配的指针。
  • 那么既然有两个指针就不会有问题了?一个在管理器列表中,一个由 get 函数返回。这听起来不像是一个 unique 指针。
  • 啊。如果您想让Get() 的调用者在不声明所有权的情况下访问该对象,我建议您改用引用:Data&amp; Manager::Get(String title) { return *List[title]; }。您也可以使用普通指针(可以通过在 unique_ptr 上调用 get() 来获得)。
猜你喜欢
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 2017-05-20
  • 2017-11-30
  • 1970-01-01
  • 2023-03-30
相关资源
最近更新 更多