0

Suppose I have code like that one, and I want to get access to the myClassB members. How can I do that? I need to use the functionality of functionA. I can't change it because it is from the 3rd party library. And I need to use functionA to create it, and get values created by it. In this case "Test_1" string

class myClassA {
public:
    myClassA(){}
    ~myClassA(){}
};

class myClassB : public myClassA
{
public:
  myClassB(){}
  void setString1(std::string newString)
  std::string getS1()     
private:
  std::string privateMember;
};

std::shared_ptr<myClassA> functionA()
{
  std::shared_ptr<myClassB> temporary(new myClassB());
  temporary->setString1("TEST_1");
  return std::move(temporary);
}

int main()
{
  std::shared_ptr<myClassA> myPtr; // = functionA(); ??
}
pZCZ
  • 183
  • 2
  • 11

2 Answers2

3

Theoretically, you could use a dynamic_cast (or in this case specifically, std::dynamic_pointer_cast to get the derived pointer type. Like so:

std::shared_ptr<MyClassA> a_ptr = functionA();
std::shared_ptr<MyClassB> b_ptr = std::dynamic_pointer_cast<MyClassB>(a_ptr);
if(b_ptr) {//Check to verify the cast was successful
    b_ptr->setString("Test1");
}

There is, however, a major caveat to this. In order for dynamic_cast (and therefore std::dynamic_pointer_cast) to work, your object hierarchy must have a virtual table defined. That means at least one of the methods defined by MyClassA must be declared virtual. The simplest solution is to declare the destructor virtual, since that's good practice whenever you're defining polymorphic objects (since you need it to ensure that any derived classes clean up their resources correctly).

class MyClassA {
public:
    MyClassA() = default;
    virtual ~MyClassA() = default;
};
Xirema
  • 19,889
  • 4
  • 32
  • 68
  • Thanks for your answer but I need to use functionA to create pointer – pZCZ May 26 '17 at 18:29
  • @pZCZ That's fine. `std::make_shared` is just syntactic sugar for what `functionA` does in its first line of code. – Xirema May 26 '17 at 18:31
  • still, dont know how to get values set by functionA in this case I need to get "TEST_1" string. Sorry for being not to specific. – pZCZ May 26 '17 at 18:35
  • 1
    @pZCZ Inside the `if` statement, after you've verified the validity of `b_ptr`, `b_ptr` is a 100% authentic pointer to an object of type `MyClassB`. It can be used the same as any other pointer to `MyClassB`. So you can call `b_ptr->getS1();`, and it'll do exactly what you expect it to do. – Xirema May 26 '17 at 18:38
  • Far more succinct and tidy in terms of scoping would be to write `if ( auto const b_ptr = std::dynamic_pointer_cast(a_ptr) ) { /* Use b_ptr */ }` – underscore_d May 26 '17 at 18:39
  • @Xirema b_ptr is a 100% authentic pointer to an object of type MyClassB but I need to have access to the object exactly returned by functionA – pZCZ May 26 '17 at 18:41
  • @pZCZ And you will. You may need to spend some time with a C++ fundamentals book, since a proper resource would have taught you this before you started working with pointers. [Some suggested reading is here](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Xirema May 26 '17 at 18:55
  • Thank you for your reply. My previous comments were for your code before you edit the first line. – pZCZ May 26 '17 at 19:05
1

Agree with dynamic_cast but without a virtual function table in ClassA, something like this would have to do:

Test This Code

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

class myClassA {
public:
    myClassA(){}
    ~myClassA(){}
};

class myClassB;
class ClassB_Registry{
    private:
    ClassB_Registry(){
    }
    ~ClassB_Registry(){
    }
    public:
    static ClassB_Registry* Get(){ static ClassB_Registry obj; return &obj; }
    static void Register(myClassB* ptr){
        Get()->mPointers.insert(ptr);
    }
    static void UnRegister(myClassB* ptr){
        Get()->mPointers.erase(ptr);
    }
    static myClassB* Cast(myClassA* ptr){
        if(Get()->mPointers.count((myClassB*)ptr) > 0) return (myClassB*)ptr;
        return nullptr;
    }

    private:
    std::set<myClassB*> mPointers;
};



class myClassB : public myClassA
{
public:
myClassB(){ ClassB_Registry::Register(this); }
~myClassB(){ ClassB_Registry::UnRegister(this); }
void setString1(std::string newString){privateMember = newString;}
std::string getS1() { return privateMember; }
private:
std::string privateMember;
};

std::shared_ptr<myClassA> functionA()
{
std::shared_ptr<myClassB> temporary(new myClassB());
temporary->setString1("TEST_1");
return std::move(temporary);
}

int main()
{
std::shared_ptr<myClassA> myPtr = functionA(); //??
std::shared_ptr<myClassA> myPtr_a(new myClassA()); //??
myClassB* pDerrived = ClassB_Registry::Cast(myPtr.get()); // bridge the RTTI gap
if(pDerrived) 
    std::cout << pDerrived->getS1();

pDerrived = ClassB_Registry::Cast(myPtr_a.get()); // works on A pointers to return null
if(pDerrived) 
    std::cout << pDerrived->getS1() << "    \n";
else
    std::cout << "Not A Pointer of Type B" << "    \n";
}

It's not pretty, but if myClassB had a virtual table as mentioned previously, and all future derived classes used myClassB as the base, then you could bridge the gap for RTTI.

James Poag
  • 2,320
  • 1
  • 13
  • 20