【问题标题】:Static members inheritance and protection静态成员继承与保护
【发布时间】:2012-09-22 18:40:01
【问题描述】:

我确实有关于静态成员继承及其在 C++ 中的保护的问题。我希望我会足够清楚,因为编写思维状态并不总是那么容易:) 我正在为一个图形程序编写一个简单的(文本解析器),该程序正在加载具有自定义格式的文本文件,现在文本部分几乎完成了并且现在我需要生成一些对象来为它们提供我从文件中加载的数据。

我相信这个问题属于 C++ 的第 1 小时,但我卡住了。例如,我从文本文件中加载了 2 种类型的逻辑“节点”,即 LAYER 和 PLINE,它们还具有它们可以或不能对两者通用的属性。 LAYER 与 PLINE 和 back 的关系现在完全无关紧要,困扰我的是如何连接和处理两者的属性:

假设我选择 DataObj 作为两者的基类。 DataObj 有一个名为“name”的成员,因为 LAYER 和 PLINE 都可以有一个名称。 LAYER 有一个属性,该属性仅对图层通用,例如。 “锁定”,并且 PLINE 具有仅对 pline 通用的属性,例如。 “颜色”。在做事的“学校方式”中,它看起来像:

/// i use const char* for everything to not complicate things ... 
...
class DataObj {
  ...
  const char* name;
  ...
}
...
class Layer : public DataObj {
 ...
 const char* locked;
 ...
}
...
class Pline : public DataObj {
 ...
 const char* color;
 ... 
}
...
int main(){
   Layer* l = new Layer();
   l.name = "first layer";
   l.locked = "false";

   Pline* p = new Pline();
   p.name = "wonderful line";
   p.color = "0xFF3300";
}
...

现在我想更“动态”地做到这一点,因为我真的不关心静态类型的成员名称(将来可能还有访问器),尤其是在为对象提供来自解析器的数据时。我的意思是只用 2 种节点类型很容易做到这一点,但我会有很多。

所以我想做的概念是“静态地”推送每个节点类型(类)的允许属性的向量,然后只检查对象中是否允许此属性并在解析期间设置它。我可能想要 2 个重要成员 1. 是 kv 对的 std::map,第二个是某些节点允许属性的静态向量。按照之前输入的代码:

...
class DataObj {
  ...
  static std::vector<const char*> allowedAttrs;
  std::map <const char*, const char*> attrs;

  private: 
     static bool isInit;
  ...
}
...
DataObj::DataObj(){
  if(!isInit)
    allowedAttrs.push_back("name");
  isInit = true;
}
...
Layer::Layer(){
  if(!isInit) // private static for Layer
     allowedAttrs.push_back("locked");
}
...
Pline::Pline(){
  if(!isInit) // private static for Pline
     allowedAttrs.push_back("color");
}
...

我遇到的问题可能从月球上可以看到。如果我们首先初始化一个新层,然后是一个新的 Pline,Pline 将在 allowedAttrs 向量中具有名称、锁定和颜色,这是不正确的,因为“锁定”应该只对层节点有效。

所以我需要一些方法来解决这个问题,即成员“allowedAttrs”对于不常见的属性(如 Layer 对象中的“锁定”)变为“私有”,但也保留其来自超类的“公共”性质“ DataObj” - 因此它可以捕获“名称”等共享属性。换句话说,我不想“破坏”到基类的“继承流”,并为每个节点类(对象)定义新变量,一遍又一遍地重复相同的代码。 (类似于虚拟变量)。

我希望这个问题不是(那么)愚蠢,并且非常感谢您的回答。

【问题讨论】:

  • 请让您的问题简明扼要。它将帮助您快速获得答案。
  • 作为基类构造函数首先被调用,其他push_backs会发生吗?
  • 我认为你完全误解了static 的工作原理
  • i use const char* for everything to not complicate things IMO 不正确。你应该改用std::string
  • const char* 有什么问题?

标签: c++


【解决方案1】:

以下是我将如何实现您的允许属性向量。我会让每种类型都包含一个允许的属性列表,在初始化期间复制它们的基类列表。

class DataObj {
  ...
  static std::vector<std::string> allowedAttrs;
  std::map <std::string, std::string> attrs;

  private: 
     static bool isInit;
  ...
}
class Layer {
  static std::vector<std::string> allowedAttrs;
  ...
};
class PLine {
  static std::vector<std::string> allowedAttrs;
}
...
DataObj::DataObj(){
  if(!isInit)
    allowedAttrs.push_back("name");
  isInit = true;
}
...
Layer::Layer(){
  if(!isInit) { // private static for Layer
     allowedAttrs = DataObj::allowedAttrs;
     allowedAttrs.push_back("locked");
  }
}
...
Pline::Pline(){
  if(!isInit) { // private static for Pline
     allowedAttrs = DataObj::allowedAttrs;
     allowedAttrs.push_back("color");
  }

}

注意事项:

  • 矢量和地图都使用std::string,而不是char*。虽然vector&lt;char*&gt; 可能有用,但map&lt;char*,char*&gt; 只是一个错误。

  • 您可以通过让查找函数更智能一些来避免复制基类的列表。

【讨论】:

  • 感谢您的回复,我真的很感激,好吧,我终于深入研究了 C++,而且大多数较旧的文档总是以某种方式在其某些变体中使用 char,所以我的想法是正确的使用它。跨度>
  • 不是char*const char* 的每次使用都是不正确的,但已经足够了。我从不向新的 C++ 程序员推荐 char*
【解决方案2】:

1) 我会为每种类型使用单独的静态成员来保存每种类型的允许属性。
2) 将静态成员移动到函数中,这样更安全,并且可能避免检查它是否一直被初始化(取决于你的编译器有多好和其他细节)
3)不要使用const char*,除非是非常具体的事情。如果您不知道这些是什么,请始终使用std::string。在这种情况下,我们必须使用std::string
4) 我将allowedAttrs 从向量更改为set,这对于大量属性可能会更快,而对于较小的数字可能会更慢。

这是基础:

class DataObj {
  const std::set<std::string>& get_allowed_data_attributes() static {    
    static std::set<std::string> allowedAttrs = {"name"};
    return allowedAttrs;
  }
  std::map <std::string, std::string> attrs;
  public:
     DataObj(){ }
     void set_attribute(std::string key, std::string value) {
         auto it = get_allowed_data_attributes().find(key);
         if (it  == get_allowed_data_attributes().end())
            throw bad_key_exception(key);
         attrs.insert(std::make_pair(std::move(key), std::move(value)));
     }
     const std::string& get_attribute(const std::string& key) const {
         auto it = attrs().find(key);
         if (it  == attrs().end())
            throw bad_key_exception(key);
         return it->second;
     }
};

这是派生的:

class Layer : public DataObj {
  const std::set<std::string>& get_allowed_data_attributes() static {    
    static std::set<std::string> allowedAttrs = {"locked"};
    return allowedAttrs;
  }
  public:
     DataObj(){ }
     void set_attribute(std::string key, std::string value) {
         auto it = get_allowed_data_attributes().find(key);
         if (it  == get_allowed_data_attributes().end())
            DataObj::set_attribute(std::move(key), std::move(value));
         else
            attrs.insert(std::make_pair(std::move(key), std::move(value)));
     }
     const std::string& get_attribute(const std::string& key) const {
         auto it = attrs().find(key);
         if (it  == attrs().end())
            return DataObj::get_attribute(key);
         else
            return it->second;
     }
};

请注意,如果您给它一个无效的密钥,它会抛出一个bad_key_exception,您必须添加它。确保它继承自 std::runtime_error

【讨论】:

  • 也感谢您提供强大的解决方案和提示。我有一个问题,set_attribute 和 get_attribute 方法可以在基类中只定义一次作为保护吗?
  • @cybercow:当然,这很好用。 (另外,我修复了代码中的一个错误)
  • 再次感谢您,真的会推动我的项目!真的没有机会使用 std::string(s),可能我的第一个假设是使用 c-strings 会产生更快的代码?我正在编写的解析器将解析大约 50 万行原始文本数据(主要是空间坐标),并像您编写的示例一样提供对象堆栈。例如,您可以有 100 个层和 100000 条线。假设 c-strings 更快,我大概是愚蠢的,但我从 c-strings 开始并且不想将它们混合在一起
  • @cybercow:cstrings 更快,但前提是你把它们弄对了,而且速度的提高就像开车前减掉 20 磅一样。如果你犯了错误,汽车就会爆炸。使用std::string。总是做对了,以后再担心速度。
猜你喜欢
  • 2014-01-30
  • 2015-06-10
  • 1970-01-01
  • 2016-06-12
  • 2014-06-09
  • 2013-05-09
  • 2023-03-14
  • 1970-01-01
  • 2018-11-30
相关资源
最近更新 更多