【问题标题】:Inherit private members from base class从基类继承私有成员
【发布时间】:2013-02-12 13:50:34
【问题描述】:

这个问题已经被问过here——实际上不止两次——但我自己无法从帖子中找到解决我的问题的方法。

我有一个库,其中有一个名为A 的类。从A 类我需要访问std::map<>,但它是私有的。此外,根据我在我提到的帖子中发现的可能性,A 类具有 no 模板函数。

我实际上能够重新编译库,这样我就可以简单地更改可见性。 但是,这将是大量的工作——我不确定更改可见性是否不会导致其他任何崩溃。

我想做的是,在课堂上B

// NOT MY CODE -- library <a.h>
class A {
private:
    std::map<int, int> A_map;
};

// MY CODE -- module "b.h"
# include <a.h>
class B : private A {
public:
    B() {
        for (auto it(A_map.begin()); it != A_map.end(); ++it) {
            ...;
        }
    }
};

如何在不更改原始类的情况下做到这一点 - 并且在基类中没有任何可用的模板化方法可以重载/专门化?

【问题讨论】:

  • 什么是template &lt;typename X, typename Y&gt; std::map&lt;X, Y&gt; map;
  • @us2012 我使用class B : private A 没有问题,但它仍然不允许我从A 访问map
  • 我的博客展示了一种方法。请不要使用我的答案中链接的实现。如果它适合你,最好使用bloglitb.blogspot.de/2011/12/…
  • @DrewDormann:我相信他的意思是,它可能只是一种方法
  • 如果你想知道我博客上“更安全”的第二篇文章是如何工作的,我推荐一个新的 SO 问题。我很确定有很多人很乐意在 SO 上解释它。

标签: c++ class templates inheritance private


【解决方案1】:

首先,将所有数据成员保密是一个非常好的主意。如果你的派生类真的需要访问A_map,那么最好的办法是给基类一个getter,它返回一个对它的const引用(假设你只需要读访问)。如果您需要对 A_map 的写入权限,那么这很好地表明您需要重新考虑您的设计。

除此之外,如果不严重违反 C++ 的规则和约定,就无法授予类 BA_map 的访问权限。

顺便说一下,私有继承还有别的意思。这意味着A 类的所有公共方法都将成为B 类的私有方法。换句话说,私有继承意味着你继承了A的实现,而不是它的接口。所以这里对你没有帮助。

【讨论】:

  • 这个库不是我的,虽然它工作得很好,但它仍在开发中——我真的很想避免对代码进行任何修改,因为即使编译它也需要时间。
【解决方案2】:

特意保护私有变量免受任何外部访问。只有拥有的类允许通过较少限制的访问或结交朋友时,您才能访问成员。

而且我不确定更改可见性是否不会导致其他任何崩溃。

C++ 中的访问限制是经过精心设计的,因此在进行名称查找时,可访问性是检查的最后一件事。这意味着如果您有一个工作程序并且您使某些东西更易于访问,那么该程序的行为根本不应该改变,因为在检查可访问性之前已经检查了所有其他潜在问题。

【讨论】:

  • 我认为这个开放可见性只会有帮助,但我实际上是想强调一个事实,即我不想弄乱我自己没有的代码' t 写的。这真的是最后的手段。
【解决方案3】:

您首先应该非常确信您正在尝试做的事情实际上是有效的......变量是私有的可能有充分的理由。修改它可能会破坏 A 实例的状态,并且无法保证私有变量在您破坏封装以读取它时将处于逻辑或一致状态。

有了这个警告,如果你可以修改/重新编译库来声明你的类是 A 的朋友,那么可能是要走的路。让 B 通过组合持有 A 的实例,并且由于它是朋友,它可以访问 A 实例的私有成员。


而且,因为我感觉特别邪恶,这里有一个演示如何在不修改 A 的情况下打破封装。虽然它不是可移植的(我在 64 位 linux 上使用 g++ 工作)并且涉及找出内存中的对象布局。为简单起见,我将地图更改为矢量。

#include<vector>
#include<iostream>

class A {
private:
    int blah; //Just to make it a bit more realistic.
    std::vector<int> A_vec;
public:
    void outputVec() {
       std::vector<int>::iterator it = A_vec.begin();
       while(it != A_vec.end()) {
         std::cout << *it << std::endl;
         ++it;
        }
    }
};


int main() {
  A* a = new A();
  std::vector<int>* v = (std::vector<int>*)(((char*)a)+sizeof(long));
  v->push_back(27);
  v->push_back(12);
  a->outputVec();
  return 0;
}

【讨论】:

  • 感谢您的帖子;我对我的问题做了一个小的修改。我真的不介意这里的可见性礼貌——我知道我在做什么,我所做的只是阅读一些我需要的数字。我正在使用的图书馆很大,并且仍在由其所有者开发。他们可能会改变做事的方式,但他们现在不会这样做——我也不想这样做。
  • @Rubens。问题是这个库的维护者明确地不保证该私有成员的内容,即使在现有版本的代码中也有你需要的数字。我已经编写了很多将数据流入的对象,并且只有在公共访问者主动请求时才将其清理为可用状态。
  • @patros 我确实同意你所说的,但我知道发生了什么,我可以在这里看到整个代码和值在我想访问它们时可用。我只是想避免更改/重新编译代码。如果只对我的代码进行修改,我会让它完全适合我的程序。
  • @patros +1 非常感谢您的回答。每次我的老师说他是邪恶的,我都会害怕,你的帖子也是如此(:虽然这对我来说是一个非常有趣的解决方案,但我担心我将无法使用它:不仅仅是因为弄清楚map 之前的 all 元素的大小会很棘手,但也因为我在分布式应用程序中运行它——我在这里最不想依赖的是内存对齐^^序列化已经用未对齐的字节洗了我的头(:
【解决方案4】:

私有继承真的是变相的组合!它实际上的意思是“实施”。

撇开这一点不谈,不管私有/公有/受保护的继承模式如何,私有成员都将保持私有(在该类之外无法访问,即使是派生的也不能访问)。

有两种合法的方式可以访问任何类的私有成员。

1) 使用 Get/Set 方法访问私有成员(假设类公开了它)。

2) 与班级交朋友。

在您的情况下,A 类属于一个库,库供应商有责任为外部世界与 A 类交互提供适当的接口。也许,最好与库供应商交谈。对我来说,它看起来更像是一个设计问题,而不是实现问题。

只是为了测试,作为一种快速破解,我建议使用好友机制。

    class A {
      friend class B;
      private:
      std::map<int, int> A_map;
    };

    // MY CODE -- module "b.h"
    class B {
     public:
     B() {
      for (std::map<int, int>::iterator  it(a.A_map.begin()); 
          it != a.A_map.end(); ++it) {  }
     }
     A a;
    };

【讨论】:

    猜你喜欢
    • 2017-04-14
    • 1970-01-01
    • 2013-08-30
    • 2020-09-04
    • 1970-01-01
    • 2020-09-01
    • 2018-05-11
    • 2013-04-11
    • 1970-01-01
    相关资源
    最近更新 更多