【问题标题】:How to access private Constructor from within inner class? C++14如何从内部类中访问私有构造函数? C++14
【发布时间】:2021-08-27 07:27:33
【问题描述】:

我正在尝试将构建器模式应用于对象,但私有构造函数在内部类中不可见。

#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 在friend 子句中不完整,所以我无法使用它。我非常想将对象的实例化限制为 Builder 类,有没有办法在 C++ 中做到这一点? 还是将 Outer 构造函数设为 public 是唯一的选择?

【问题讨论】:

  • 问题是std::make_unuque需要访问你的私有构造函数,它不能,因为它既不是朋友也不是成员。
  • stackoverflow.com/a/5013735/1467600 另有说法,但你对 c++14 是正确的..(在 c++14 中与 pre c++11 有相同的错误)
  • 在 C++03 中,您需要使内部类成为朋友以引用私有 ctor,但您的内部类无论如何都不会恢复到 ctor。 std::make_unique 会。
  • @DavidTóth 你错过了n. 1.8e9-where's-my-share m. 写给你的重点。从内部类访问外部类没有问题,反之亦然,模板std::make_unique不能访问非公共构造函数是问题。
  • 哦,我错过了!对...

标签: c++ c++14 inner-classes builder


【解决方案1】:

这不起作用,因为真正的建造者是std::make_unique,它既不是朋友也不是会员。让它成为朋友是不可能的,因为你不知道它委托给什么内部函数,而且无论如何它都会破坏私有构造函数的目的。

您可以只使用裸new 而不是std::make_unique,它会在紧要关头工作。如果你想要一个共享指针而不是唯一指针,这会变得有点问题,因为性能不会那么好。

下面是如何使它适用于unique_ptrshared_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 的成员访问权限。

【讨论】:

    【解决方案2】:

    怎么样:

    #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++ 中创建友元函数。

    【讨论】:

    • 现在你可以在main 中说std::unique_ptr&lt;Outer&gt; instance2 = std::make_unique&lt;Outer&gt;();。那么,有一个建设者的目的是什么?
    • 示例中不多,但在实际代码使用中,构造函数参数不同,所以这是我接受的答案。
    • @DavidTóth 接受你想要的,但“构造函数参数不同”与我的言论无关。如果您有std::make_unique 作为朋友,您可以使用任何构造函数参数绕过Builder。只需将构造函数公开即可。
    • 如果使用friend,我会使用friend template&lt; class T, class... Args &gt; unique_ptr&lt;T&gt; std::make_unique( Args&amp;&amp;... args );
    • @MarekR 它不能用 gcc 编译:friend template&lt; class T, class... Args &gt; unique_ptr&lt;T&gt; std::make_unique( Args&amp;&amp;... args );,我想出的有点奇怪但可以编译:template &lt;typename T, typename... Args&gt; friend typename std::_MakeUniq&lt;T&gt;::__single_object std::make_unique(Args&amp;&amp;... args);
    【解决方案3】:

    取决于未在问题中描述的细节,还有其他方法可以解决问题。

    例如,如果虚拟调用适合您,那么最简单的方法就是使用工厂模式。这将完全隐藏实现细节(这是使用依赖注入时的常用方法)可以构造对象,因为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;
    }
    

    【讨论】:

      猜你喜欢
      • 2011-05-08
      • 2017-09-10
      • 2011-02-05
      • 2017-07-13
      • 1970-01-01
      • 1970-01-01
      • 2021-04-17
      • 2018-02-19
      • 2016-08-04
      相关资源
      最近更新 更多