【问题标题】:How to use a std::string with inheritance as parameter?如何使用带有继承的 std::string 作为参数?
【发布时间】:2013-06-26 00:16:29
【问题描述】:

我目前正在使用 C++ 进行一个大学项目,我的一项任务是使用继承和多态性创建一个社交网络。目前我有一个在 MapMultimap 上使用的 Node 类(两者都是手动创建的,而不是从 std 中使用)。该节点可以保存两个变量(例如 keydata),在我使用它的地方,第一个变量可以是指针或字符串(它们让我们使用 std::string).

我遇到的问题是,当我从“根”类(Object)继承并使用“Object”作为“key”的数据类型时,我无法通过使用 std 作为其构造函数的参数创建的字符串,因为它不是从我的 Object 类继承的。一种解决方案是实现我自己的字符串类并使其继承自 Object,但我正在寻找其他解决方法。

如果上面的逻辑有任何问题,请告诉我,因为我刚刚开始使用 C++。

编辑 1(我的节点的一些代码):

class TempNode
{
    private:
    TempNode* next;
Key key;
T value;
public:
TempNode();
explicit TempNode(const Key thisKey, const T thisValue, TempNode* thisNext = NULL)
: key(thisKey)
, value(thisValue)
, next(thisNext)
{
}
inline Key getKey() { return key; }
inline T getValue() { return value; }
inline TempNode* getNext() { return next; }
inline void setNext(TempNode* thisNext) { next = thisNext; }
};

string 或 Person 类型目前仅在 key 中使用,但这是使用模板的另一个实现(效果很好),但我的老师现在要求我们将继承应用于整个项目(我想习惯了)。

【问题讨论】:

  • 请使用一些示例代码来展示您当前正在尝试的内容,即使您知道这是错误的。
  • 您可以将std::string 包装在派生自Object 的某个类中,因此这是可能的。
  • @aschepler 我添加了我的 Node 类和更多解释。
  • 那么KeyT 是固定类型吗?还是缺少模板声明?
  • @DyP 它丢失了,因为我目前正在编辑它并忘记添加它,但是是的,该代码用于模板中,我试图做的是添加 Object 作为键的类型,但是当我想使用 std 中的字符串作为节点的键时,就会出现问题。

标签: c++ inheritance polymorphism stdstring


【解决方案1】:

要使用继承来实现这一点,您可以将Key 视为一种数据类型,专门设计为您的地图/多地图实施中的键。 Key 继承自 Object,但它可能提供自己的特定于键的函数,例如 - 例如 - 一个函数 repr(),它生成地图用于某些特定于地图的操作的表示(可能作为散列或排序等的基础)。

映射/多重映射必须以这样一种方式使用,即Key 对象存储为指针(或std::unique_ptr,或std::shared_ptr,或任何适当的),但不能作为Key 对象的副本.

所以我们有:

struct Object
{
  virtual ~Object()
  { }
};

/* Key class. Pointers of this type are inserted
   into the map. */    
class Key : public Object
{
public:
  /* Must be supported by all keys: */
  virtual std::string repr() const = 0;
};

我们还假设Person 对象有一个单独的定义:

struct Person : Object
{
  Person(const std::string &name)
    : name_(name)
  { }

  std::string name_;
};

根据您的规范,Key 有两种风格:一种表示字符串并且必须使用字符串进行初始化,另一种表示人员并且必须通过人员指针初始化(我假设person-keys 实际上并不拥有这些指针,因此您需要确保他们指向的 person 对象只要 person-key 存在就保持活动状态。

我们通过将Key 特化为两个派生类来实现这一点,一个PersonKey 和一个StringKey

class PersonKey : public Key
{
public:
  PersonKey(Person *person_ptr)
    : Key() , person_ptr_(person_ptr)
  { }

  virtual std::string repr() const
  {
    if (person_ptr_ != 0)
      return std::string("Person/") + person_ptr_->name_;
    else
      return "<NUL>";
  }

private:
  Person *person_ptr_;
};

class StringKey : public Key
{
public:
  StringKey(const std::string &str)
    : Key() , str_(str)
  { }

  virtual std::string repr() const
  {
    return str_;
  }

private:
  std::string str_;
};

当您插入地图/多重地图时,您会生成Key 对象(您表示为Key*Key&amp;std::unique_ptr&lt;Key&gt;)。当你想插入一个字符串时,你将它们生成为StringKey对象,当你想将它们作为人指针插入时,你使用PersonKey——但是你插入的键的数据类型不会反映专业化。

这里是一个通用Key对象的例子(实现为std::unique_ptr&lt;Key&gt;,但如果你不怕内存泄漏,你可以直接使用Key*):

int main()
{
  /* General key object: */
  std::unique_ptr<Key> mykey;

  /* Now it points to a string-key, initialized using
     a string, as required: */
  mykey.reset(new StringKey("hello"));
  std::cout << "repr(mykey) == \""
            << mykey->repr()
            << '"'
            << std::endl;

  /* Now the same key object is made to refer to
     a person object: */
  Person person("me");
  mykey.reset(new PersonKey(&person));
  std::cout << "repr(mykey) == \""
            << mykey->repr()
            << '"'
            << std::endl;

  return 0;
}

上面代码的必要标题是:

#include <iostream>
#include <memory>
#include <string>

(但memory是我使用std::unique_ptr时才需要的,实际上并不是解决你的问题所必需的。)

【讨论】:

  • 谢谢!这似乎是一种更好的实现方式。
【解决方案2】:

我认为您真正需要的是模板。正如您在标准对象和外部库中看到的那样,您使用“根对象”的解决方案将无法使用,而且您将无法将容器与原语一起使用(例如,个人 id(as int) 作为键,将 Person 类作为值)。

使用模板,您可以在编译时说出要使用的类型,编译器将帮助您遵守自己的规则。可以这样声明:

template<class T1, class T2>
class Map{
  T1 key;
  T2 value;
  (...)
}

那么你或多或少可以像这样使用它:

Map<std::String, int> phoneBook;

如果您尝试向 Map 添加例如 float 而不是 int,编译器会保护您并发出警告。但是在你开始编码之前,我建议你先阅读一些教程,或者甚至是一些关于 c++ 的书。但如果你现在想从通用开始,你可以开始她:

http://www.cplusplus.com/doc/tutorial/templates/

【讨论】:

  • 哦,我已经使用 Map、Multimap、Node 和 Graph 的模板实现了整个项目,但现在的任务是使用继承。
【解决方案3】:

您能够在 Object 变量中存储字符串的唯一方法是,如果字符串类继承自您的 Object 类,那么不幸的是,您将不得不实现自己的 String 类。

这里真正的缺陷是您采用 Java/C# 方法进行设计,其中 Object 变量可以保存任何内容。在 C++ 中,处理此类事情的正确方法是使用模板,假设您的 Map/Multimap/Node 只需要保存一种特定的数据类型。

如果您的容器需要能够保存任意数据类型,我建议使用类型擦除,尽管这对于初学者来说可能有点复杂。

【讨论】:

  • 正如我在上一个答案中评论的那样,我已经做了一个完全使用模板的工作版本,但我的老师现在要求取消它,并使用继承来完成作业,他会不会有点偏离 -重点?
  • @JulioCalderón 如果你的老师想让你学习继承.. 但恕我直言,这不是最好的例子(这是你宁愿使用模板的地方——标准容器库因此包含模板)。
  • 哦,好的。我想这不是使用它的最佳方式,因为使用它并没有什么好处,我将创建自己的字符串类,因为我没有太多时间来上交它.
猜你喜欢
  • 2019-03-22
  • 1970-01-01
  • 2023-03-26
  • 2012-11-17
  • 1970-01-01
  • 2013-12-29
  • 2021-10-27
  • 1970-01-01
  • 2013-08-20
相关资源
最近更新 更多