【问题标题】:error: 'A' is an inaccessible base of 'B'错误:“A”是“B”的不可访问的基础
【发布时间】:2017-01-12 22:28:22
【问题描述】:

我的代码如下-

#include <iostream>
#include <string>

class A{
    int a;
    public: virtual void sayHello(){ std::cout << "Hello\n"; }
};

class B : private A{
    std::string name;
  public:
    B(std::string _n): name(_n){}
    void sayName(){std::cout << name << "says hello\n";}
    void sayHello(){sayName();}
};


int main() {
    A *ptr = new B("c++");
    ptr->sayHello();
    return 0;
}

产生以下编译器输出 -

错误:

prog.cpp: In function 'int main()':
prog.cpp:20:22: error: 'A' is an inaccessible base of 'B'
  A *ptr = new B("c++");
                      ^

正如之前的回答 - hereherehere,我知道如何解决这个问题。通过使用public 继承而不是privateprotected

但是如果我真的想在基类后面隐藏一些接口,难道没有其他方法可以做到这一点吗?还是按照c++ lang规范是不可能的。

【问题讨论】:

  • 具体来说,您想做什么?您的问题是您不希望通过B 访问A,但您确实希望通过B 访问A。我怀疑是设计问题...
  • 好吧,您将 A 的继承设为私有。所以没有人知道 B 继承自 A。可能有一些私有或受保护继承的边缘情况,但不是这里。而将 B 分配给 A* 的事实意味着您不想隐藏界面。
  • 仅当AB 的“可访问基”时,才允许使用A* 指向B 类型的对象。如果您想隐藏AB 的基这一事实,那么用户用指向A 的指针来持有它是没有意义的。
  • "但是如果我真的想在基类后面隐藏一些接口,难道没有其他方法可以做到这一点吗?" 如果你允许人们转换@ 987654341@ 到A*,那么界面不会被隐藏,对吗?您的问题似乎基于矛盾。也许您可以提供更多详细信息,说明您正在尝试完成的工作。
  • @YehezkelB。 - 不是指针的使用是不允许的;这是B*A*转换。例如,如果该类提供了一个成员函数A* get_base_pointer() { return this; };,那么使用结果指针不会有任何问题。

标签: c++ class oop c++11 inheritance


【解决方案1】:

有一个难看的方式来解决这个问题:C 风格的强制转换。 C 风格的转换可以转换为不可访问的基类。这是 C 风格转换可以做 C++ 转换不能做的事情的唯一一种情况。从 cppreference 开始,当 C 风格转换 (T) foo 尝试执行 static_cast&lt;T&gt;(foo) 时,它可以做的不仅仅是 static_cast

[P]指向派生类的指针或引用还允许转换为指向明确基类的指针或引用(反之亦然)即使基类不可访问(即,此转换忽略私有继承说明符)。

强调

因此,您可以这样做:

int main() {
    A *ptr = (A *) new B("c++");
    ptr->sayHello();
    return 0;
}

这是丑陋的,它带来了强制转换的所有缺点,尤其是 C 风格的强制转换。但它确实有效,而且是允许的。

Live on Wandbox

【讨论】:

    【解决方案2】:

    尽管我发现隐藏基类并想将B 转换为A 很奇怪,但您可以使用operator A*()
    它遵循一个最小的工作示例:

    #include <iostream>
    #include <string>
    
    class A{
        int a;
    public:
        virtual void sayHello(){ std::cout << "Hello\n"; }
    };
    
    class B : private A{
        std::string name;
    public:
        B(std::string _n): name(_n){}
        operator A*() { return this; }
        void sayName(){std::cout << name << "says hello\n";}
        void sayHello(){sayName();}
    };
    

    现在您可以将其用作:

    int main() {
        A *ptr = *(new B("c++"));
        ptr->sayHello();
        return 0;
    }
    

    甚至更好:

    int main() {
        B b{"c++"};
        A *ptr = b;
        ptr->sayHello();
        return 0;
    }
    

    将演员表添加到A&amp; 就像添加定义为return *this; 的成员方法operator A&amp;() 一样简单。

    【讨论】:

    • 这种方法有什么问题吗?
    • @AbhinavGauniyal 我发现getA 更容易使用,主要是在将对象作为函数参数传递时,但有人可能会说不清楚幕后发生了什么。我不知道,这是一种替代方法,是否有用取决于您。 ;-)
    • @AbhinavGauniyal 哦,是的,当您将指向B 的指针分配给指向A 的指针时,它不会被隐式调用,因为它是B 的成员方法,您必须拥有B 的实例或对实例的引用,如答案中的示例所示。
    • 并且内存将在堆栈上进行管理,对吗?只要B b{"c++"} 还活着,就不需要删除..
    • @AbhinavGauniyal 这是真的,但这是一个例子。你可以做任何你想做的事,看看我仍然使用new的第一个例子。
    【解决方案3】:

    如果你想让多态指针转换在类外工作,那么继承必须是公共的。没有办法解决这个问题。

    您可以在类中添加一个成员函数来进行多态指针转换:

    class B : private A{
        // ...
    public:
        A* getA() {
            return this;
        }
    };
    

    这允许您这样做,同时仍然允许私有继承:

    B* b_ptr = new B("c++");
    A* ptr   = b_ptr->getA();
    // ptr   = b_ptr; // only allowed in member functions
    

    我还没有遇到过这种技巧有用但适合你自己的现实世界设计。


    PS。请记住,您应该销毁您创建的对象。还要意识到delete ptr 具有未定义的行为,除非~A 是虚拟的。

    【讨论】:

    • “没有办法解决这个问题。”这实际上是不真实的;有一个解决方法:C 风格转换
    【解决方案4】:

    当您想隐藏 AB 的基时,这是有效的。

    但是你的任务

    A *ptr = new B("c++");
    

    打破了这种隐藏,因为你使用了A*。所以c++会产生一个错误,因为依赖是隐藏的。你可以这样做

    B *ptr = new B("c++");
    

    虽然。

    【讨论】:

      猜你喜欢
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-07
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      相关资源
      最近更新 更多