1

I have some code that looks something this:

class Info {
  public:
    virtual bool IsHere() = 0;
    virtual std::wstring GetStr() = 0;
};

class WindowsInfo : public Info {
  public:
    virtual std::wstring GetAnotherStr() = 0;
    bool IsHere() override;
};

class AdvancedWindowsInfo : public WindowsInfo {
  public:
    AdvancedWindowsInfo() {}
    ~AdvancedWindowsInfo() {}

    std::wstring GetAnotherStr() override;
    std::wstring GetStr() override;
};
  
class InfoFactory {
  public:
    static Info* GetInfo();
};
  
class InfoManager {
  public:
    InfoManager();
    //~InfoManager();

    bool IsSomething();

  private:
    std::unique_ptr<Info> info;
};
  
InfoManager::InfoManager() {
  #if WIN
    info = std::make_unique<WindowsInfo>();
  #else  // currently no implementation Linux
    info = nullptr;
  #endif
}
  
bool InfoManager::IsSomething() {
    std::unique_ptr<Info> info = InfoFactory::GetInfo();

    return info && info->IsHere();
}
  
Info* InfoFactory::GetInfo() {
  #if IS_WINDOWS
    return new AdvancedWindowsInfo();
  #else
    return nullptr;
  #endif
}

The entire code is too large (and confidential) to post here, but this snippet sums it up pretty well.

Essentially, I have a base class and some derived classes.

I also have a manager that uses a (smart) pointer to that base class.

And a Factory Method that returns the appropriate Derived object (although the signature returns a Base*).

Unfortunately, I can't get the the assignment (via the Factory Method) to work. I've tried multiple approaches but nothing works.

I tried using unique_ptr and make_unique<raw pointer>() --> it doesn't work with derived classes, only base.

I tried using unique_ptr and raw pointers --> conversion is not possible.

I tried using raw pointers (although I don't want this) and raw pointers --> it tells me that the destructor is called on the base object which is abstract. How can you call a destructor when you haven't instantiated the object (since it's an abstract class)? The compiler is contradicting itself!

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
conectionist
  • 2,694
  • 6
  • 28
  • 50
  • 1
    Can't you have `InfoFactory::GetInfo()` return a `std::unique_ptr`? That would be more recommended. – Galik Jan 25 '23 at 17:48
  • 2
    "Doesn't work" and "Can't get it working" are not descriptive. Do you get a compiler error? Does it crash? Do you get unexpected results? If you get an error, what error do you get? – François Andrieux Jan 25 '23 at 17:52
  • 1
    Also, `std::unique_ptr info = InfoFactory::GetInfo();` creates a _local_variable info which goes out of scope when the function returns. It leaves the member variable info unaffected. Perhaps what you intended instead was `info.reset( InfoFactory::GetInfo() );` – Avi Berger Jan 25 '23 at 17:59
  • 3
    "How can you call a destructor when you haven't instantiated the object..." Did you give the real `Info` a `virtual ~Info() = default;` destructor? You didn't give the shown `Info` one. See [here](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors) – Nathan Pierson Jan 25 '23 at 18:01

2 Answers2

3

Let's check the documentation for std::unique_ptr's constructors. The signature of the relevant constructor:

explicit unique_ptr( pointer p ) noexcept; (2)

The converting constructor that converts a raw pointer to a std::unique_ptr is explicit. Among other things, this means it cannot be used for copy initialization of the form

std::unique_ptr<Info> info = InfoFactory::GetInfo();

Instead, you can use direct initialization:

std::unique_ptr<Info> info{InfoFactory::GetInfo()};

Which will allow you to perform that conversion.


While looking at this code, however, I notice that the local variable info in InfoManager::IsSomething is shadowing the class member variable InfoManager::info. If you want to change an existing std::unique_ptr so that it's now managing a new raw pointer, you might want to use reset:

info.reset(InfoFactory::GetInfo());
Nathan Pierson
  • 5,461
  • 1
  • 12
  • 30
  • If you want the conversion at all. There is room for failure between `new AdvancedWindowsInfo();` and safe storage in `info` that would be solved by an end-to-end `unique_ptr` curtesy of [`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique). – user4581301 Jan 25 '23 at 18:16
0

Ok, so I did the following:

  1. InfoFactory::GetInfo() now returns a std::unique_ptr<Info>, as indicated by Galik
  2. Added virtual ~Info() = default; as indicated by Nathan Pierson

Now everything seems to be working ok. For now, I will the question unanswered as I still need to run some tests and double check some things, but basically it seems to be ok.

Thank you to everyone who made positive contibutions!

conectionist
  • 2,694
  • 6
  • 28
  • 50