问题描述
我正在尝试将构建器模式应用于对象,但私有构造函数在内部类中不可见。
#include <iostream>
#include <memory>
class Outer{
private:
Outer(void){ std::cout << "Constructed!" << std::endl; }
public:
class Builder{
public:
std::unique_ptr<Outer> build(void){
return std::make_unique<Outer>();
}
};
};
int main(int argc,char** agrs){
std::unique_ptr<Outer> instance = Outer::Builder().build();
return 0;
}
失败并出现以下错误:
In file included from /usr/include/c++/8/memory:80,from scrap.cpp:2:
/usr/include/c++/8/bits/unique_ptr.h: In instantiation of ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = Outer; _Args = {}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<Outer>]’:
scrap.cpp:11:35: required from here
/usr/include/c++/8/bits/unique_ptr.h:831:30: error: ‘Outer::Outer()’ is private within this context
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
scrap.cpp:6:2: note: declared private here
Outer(void){ std::cout << "Constructed!" << std::endl; }
^~~~~
我在定义中尝试了 friend class Outer::Builder
,但是 Outer
在朋友子句中是不完整的,所以我无法让它工作。我非常想将对象的实例化限制为 Builder
类,在 C++ 中有没有办法做到这一点?
还是将 Outer
构造函数设为公开是唯一的选择?
解决方法
这不起作用,因为真正的构建器是 std::make_unique
,它既不是朋友也不是成员。让它成为朋友是不太可能的,因为你不知道它委托给什么内部函数,无论如何它都会破坏私有构造函数的目的。
您可以只使用裸 new
而不是 std::make_unique
,它会在紧要关头工作。如果你想要一个共享指针而不是唯一指针,这会变得有点问题,因为性能不会那么好。
以下是如何使其适用于 unique_ptr
、shared_ptr
或任何其他类型的句柄。
#include <memory>
class Outer
{
private:
Outer();
public:
class Builder
{
private:
class InnerOuter;
public:
std::unique_ptr<Outer> build();
};
};
class Outer::Builder::InnerOuter : public Outer
{
public:
using Outer::Outer;
};
std::unique_ptr<Outer> Outer::Builder::build()
{
return std::make_unique<InnerOuter>();
}
现在只有 Outer::Builder
可以引用(并构造)一个 InnerOuter
,因为它是一个私有类。但是它的构造函数是公共的,所以 std::make_unique
可以访问它。
注意,InnerOuter
可以访问 Outer
的私有构造函数,因为它是 Outer
成员的成员,并且拥有 Outer
的成员访问权限。
关于:
#include <iostream>
#include <memory>
class Outer{
private:
Outer(void){ std::cout << "Constructed!" << std::endl; }
public:
friend class std::unique_ptr<Outer> std::make_unique<Outer>();
class Builder{
public:
std::unique_ptr<Outer> build(void){
return std::make_unique<Outer>();
}
};
};
int main(int argc,char** agrs){
std::unique_ptr<Outer> instance = Outer::Builder().build();
return 0;
}
您也可以在 C++ 中创建友元函数。
,根据问题中未描述的细节,还有其他方法可以解决问题。
例如,如果虚拟调用适合您,最简单的方法就是使用工厂模式。这将完全隐藏实现细节(这是使用依赖注入时的常用方法)对象可以构造,因为 Outer
是完全抽象的。
// header
#include <memory>
class Outer {
public:
virtual ~Outer();
virtual void fun1() = 0;
virtual int fun2(int) = 0;
class Builder{
public:
std::unique_ptr<Outer> build(void);
};
#include "Outer.h"
Outer::~Outer() = default;
class ImplOuter : public Outer {
public:
void fun1() override {
// todo
}
int fun2(int) override {
// todo
return 0;
}
};
std::unique_ptr<Outer> Outer::Builder::build(void)
{
return std::make_unique<ImplOuter>();
}
#include <iostream>
#include "Outer.h"
int main(int argc,char** agrs){
auto instance = Outer::Builder().build();
instance->fun1();
return 0;
}