7

I've a code as follows -

#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;
}

which produces the following compiler output -

Error:

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

As previously answered - here, here & here, I know how to solve this issue. By using public inheritence instead of private or protected.

But if I really really want to hide some interface behind the base class, isn't there some other way to do this? Or is it impossible to do so according to c++ lang specification.

Community
  • 1
  • 1
Abhinav Gauniyal
  • 7,034
  • 7
  • 50
  • 93
  • 4
    What, specifically, do you want to do? Your question says that you don't want `A` to be accessible through `B` but that you do want `A` to be accessible through `B`. I suspect a design problem... – Pete Becker Sep 05 '16 at 14:15
  • 4
    Well you made the inheritance to A private. So no-one knows that B inherits from A. There may be some edge-cases for private or protected inheritance but not here. And the fact that you assign B to a `A*` means you don't want to hide the interface. – Hayt Sep 05 '16 at 14:16
  • The usage of `A*` to point to an object of type `B` is allowed only if `A` is an "accessible base" of `B`. If you want to hide the fact the `A` is a base of `B`, it doesn't make sense for the user to hold it with a pointer to `A`. – Yehezkel B. Sep 05 '16 at 14:16
  • You may find [this](http://stackoverflow.com/questions/27492132/how-can-i-remove-refactor-a-friend-dependency-declaration-properly) helpful. – πάντα ῥεῖ Sep 05 '16 at 14:17
  • 1
    "*But if I really really want to hide some interface behind the base class, isn't there some other way to do this?*" If you allowed people to convert `B*` to `A*`, then the interface *wouldn't be hidden*, would it? Your question seems based on a contradiction. Perhaps you could provide more details on exactly what you're trying to accomplish. – Nicol Bolas Sep 05 '16 at 14:19
  • 2
    @YehezkelB. - it's not the **use** of the pointer that isn't allowed; it's the **conversion** of a `B*` to an `A*`. If, for example, the class provided a member function `A* get_base_pointer() { return this; };` there would be nothing wrong with using the resulting pointer. – Pete Becker Sep 05 '16 at 14:23
  • @PeteBecker Yes, "conversion" is the correct and accurate word here. Your example, BTW, still needs to point the accessibility (or lack of it). From inside `B`, `A` is accessible, of course. – Yehezkel B. Sep 05 '16 at 14:27
  • @PeteBecker indeed this is a design problem from my end, I'm still tinkering with it. I've a base class which can be instantiated, as well as its derived classes too. Now I've a few public class methods in base class that will be used in derived classes, but I don't want them to be exposed to derived classes' objects. – Abhinav Gauniyal Sep 05 '16 at 14:29

4 Answers4

6

If you want polymorphic pointer conversion to work outside the class, then the inheritance must be public. There is no way to work around that.

You could add a member function that does the polymorphic pointer conversion within the class:

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

Which allows you to do this, while still allowing private inheritance:

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

I haven't encountered a real world design where this trick would be useful, but suit yourself.


PS. Remember that you should destroy objects that you create. Also do realize that delete ptr has undefined behaviour, unless ~A is virtual.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • "There is no way to work around that." This is actually untrue; there is a workaround: C style casts – Justin Nov 01 '17 at 20:05
2

Even though I find it quite strange to hide the base class and want to cast B to A, you can use for that the operator A*().
It follows a minimal, working example:

#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();}
};

Now you can use it as:

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

Or even better:

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

Adding the cast to A& is as easy as adding the member method operator A&() defined as return *this;.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • any gotchas with this method? – Abhinav Gauniyal Sep 05 '16 at 15:12
  • @AbhinavGauniyal I find it easier to use in regard of `getA`, mainly when passing the object as a function argument, but one can argue that's unclear what's going on under the hood. I don't know, this is an alternative, it's up to you if you find it useful or not. ;-) – skypjack Sep 05 '16 at 15:15
  • @AbhinavGauniyal Oh, yeah, it is not implicitly called when you assign a pointer to `B` to a pointer to `A`, for it's a member method of `B` and you have to have an instance or a reference to an instance of `B`, as shown in the example in the answer. – skypjack Sep 05 '16 at 15:17
  • and memory will be managed on stack, right? no call for delete necessary as long as `B b{"c++"}` lives.. – Abhinav Gauniyal Sep 05 '16 at 15:20
  • @AbhinavGauniyal This is true, but it's an example. You can do whatever you want, see the first example where I still use `new`. – skypjack Sep 05 '16 at 15:21
2

There is an unsightly way around this: C style casts. C style casts can cast to inaccessible base classes. This is the one and only case where C style casts can do something that C++ casts can't. From cppreference, when a C style cast (T) foo attempts to perform static_cast<T>(foo), it can do slightly more than just a static_cast:

[P]ointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier).

Emphasis added

Thus, you can do this:

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

This is ugly, and it comes with all the disadvantages of casting and especially of C style casts. But it does work, and it is allowed.

Live on Wandbox

Justin
  • 24,288
  • 12
  • 92
  • 142
0

When you want to hide that A is a base of B this is valid.

But you assignment

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

breaks this hiding, because you use an A*. So c++ generates an error because the dependency is hidden. You can do

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

though.

Hayt
  • 5,210
  • 30
  • 37