库管理对象的生命周期,使用智能指针还是原始?

时间:2017-06-27 15:36:17

标签: c++ oop c++14

为侧面项目滚动我自己的GUI库。重构使用智能指针;但是,我遇到了一个问题。

我知道您不希望出于明显的原因而跨DLL边界使用智能指针。但是在应用程序代码中使用“new”感觉很脏。见下文:

// MYFINANCEAPP.H
class MyFinanceApp : public Application
{

MyFinanceApp() : mMainWindow(make_unique<Window>())
{
  mMainWindow->AddControl(*(new Button("testButton")));
}

private:
  std::unique_ptr<Window> mMainWindow;
};

// WINDOW.H
class Window
{
public:
  void AddControl(Control& control)  //QUESTION: HOW DO I GET SMART POINTER HERE???
  {
    mControls.emplace_back(&control)
  }
private:
  std::vector<std::unique_ptr<Control>> mControls;  //Want to use smart pointers so I am not responsible for managing...
};

我最好使用C ++ 98样式和语义并自己处理它。显然我不希望跨接口边界传递智能指针(即AddControl),但我不想负责处理控件的生命周期。

此外,使用new Button("testButton")感觉很脏。

1 个答案:

答案 0 :(得分:1)

提醒您,如果您只是保证您不会发布在与主要可执行文件不同的编译器/版本/平台上编译的DLL,则可以规避ABI问题。

无论如何,关于您的界面设计:

void AddControl(Control& control) {
    mControls.emplace_back(&control)
}

你在这里遇到了问题,因为

  • Control是多态的(或者至少看起来基于你提供的代码),这意味着你必须通过引用或指针传递来获得一个完整的&#34;对象,但
  • 在将用户传递给应用程序之前,您不希望公开一个用户必须维护原始指针的界面,然后再将其传递给您的应用程序。

这是我围绕这个问题设计的方法:

class Window {
public:
    void AddControl(std::unique_ptr<Control> control) {//Note we're passing by value!
        mControls.emplace_back(std::move(control));
    }
private:
    std::vector<std::unique_ptr<Control>> mControls;
};

然后,在您的申请中:

class MyFinanceApp : public Application {
public:
    MyFinanceApp() : mMainWindow(make_unique<Window>()) {
        mMainWindow->AddControl(std::make_unique<Button>("testButton"));
    }

private:
    std::unique_ptr<Window> mMainWindow;
};

请注意,这并不一定会阻止用户做一些愚蠢的事情,比如

std::unique_ptr<Window> window_ptr = std::make_Unique<Window>();
Button * button = new Button("This won't be properly deleted!");
window_ptr->AddControl(std::unique_ptr<Button>{button});
delete button; //Whoops!

......但是,首先没有什么可以阻止他们这样做。

另一种选择是拥有一个&#34;工厂&#34;与控件相关联。我们首先需要对AddControl进行修改:

Control & AddControl(std::unique_ptr<Control> control) {
    mControls.emplace_back(std::move(control));
    return *mControls.back();
}

struct ControlFactory {
    static Button & create_button(Window & window, std::string button_text) {
        std::unique_ptr<Button> button_ptr = std::make_unique<Button>(button_text);
        Button & ref = *button_ptr;
        window.AddControl(std::move(button_ptr));
        //ref will NOT be invalidated, because the object will still exist in memory,
        //in the same location in memory as before. Only the unique_ptr will have changed.
        return ref;
    }
};

然后,您只需更改所有Control子类上的访问修饰符,以便不允许最终用户程序员直接访问其构造函数。这样的事情可能就足够了:

class Button : public Control {
    /*...*/
protected:
    Button() {/*...*/}
    Button(std::string text) {/*...*/}
    friend class ControlFactory; //Allows ControlFactory to access, even though access is protected.
};

然后你的用户会在它们的末尾存储引用,这比指针更安全,但它确实意味着你需要保证那些引用永远不会超过应用程序本身。