【问题标题】:Friend function is unable to construct a unique pointer of the classFriend函数无法构造类的唯一指针
【发布时间】:2015-07-05 22:29:35
【问题描述】:

我有一定的设计策略,我的类的构造函数是私有的,只能由类的朋友构造。在朋友函数中,我正在尝试使用std::make_unique创建我的类的唯一指针,但它无法编译。我的 VC12 编译器报错

c:\program 文件 (x86)\microsoft visual studio 12.0\vc\include\memory(1639):错误 C2248:“Spam::Spam”:无法访问在“Spam”类中声明的私有成员

编译失败的相关代码如下

#include <memory>
class Spam {
public:
    friend void Foo();

private:
    Spam(int mem) :mem(mem) {}
    int mem;
};
void Foo() {
    std::unique_ptr<Spam> spam = std::make_unique<Spam>(10);
}

为什么我无法编译?

【问题讨论】:

    标签: c++ c++11 friend unique-ptr


    【解决方案1】:

    这是我见过的另一种方法,显然被称为 passkey idiom:让公共构造函数需要私有访问令牌。

    class Spam {
        struct Token {};
        friend void Foo();
    public:
        Spam(Token, int mem) : mem(mem) {}
    
    private:
        int mem;
    };
    
    void Foo() {
        std::unique_ptr<Spam> spam = std::make_unique<Spam>(Spam::Token{}, 10);
    }
    
    void Bar() {
        // error: 'Spam::Token Spam::token' is private
        // std::unique_ptr<Spam> spam = std::make_unique<Spam>(Spam::Token{}, 10);
    }
    

    【讨论】:

    • 代码编译,但没有链接错误“undefined reference to `Spam::token'”
    • @MathieuDutourSikiric 哇,花了三年时间才有人注意到...已修复,谢谢!
    • 我不确定你是否需要 Spam::Token 前面的 {}
    • @Caleth 如果不是std::make_unique 需要转发它,我不会这样做,因此知道它的类型:)
    • 感谢您的更正,但我想知道它如何解决最初的问题。最初的问题是构造函数是私有的并且 make_unique 与 Spam 不友好。在您的代码中,构造函数是公共的。这当然解决了这个问题,但是 Spam::Token 对此有何帮助?
    【解决方案2】:
    Why am I not able to compile?
    

    您无法编译,因为make_unique 不是Spam 的朋友。

    使make_unique 成为朋友的另一种解决方案是将unique_ptr 的创建移至Spam

    class Spam {
       ...
    private:
       Spam(int) {}
    
       static unique_ptr<Spam> create( int i ) 
       { return std::unique_ptr<Spam>( new Spam(i) ); }
    };
    

    然后让Foo 调用它。

    void Foo() {
        std::unique_ptr<Spam> spam = Spam::create(10);
        ...
    }
    

    【讨论】:

    • 这比直接在Foo 中使用new 有什么好处?
    • 使用这种结构是为了强制 API 的用户进入 RAII。这样就更难造成内存泄漏。调用 Foo 将删除垃圾邮件,如果稍后在 Foo 内部抛出异常,则垃圾邮件将被删除。
    • 但你并没有强迫 RAII:Foo 仍然可以调用 new,因为它是朋友。它可能决定在任何情况下使用unique_ptr。关键是make_unique 比new 更可取。如果您打算放弃它,那么我看不到这种方法增加复杂性的好处。
    【解决方案3】:

    在您的情况下,函数 make_unique 正在尝试创建 Spam 的实例,而该函数不是朋友。从友元函数内部调用非友元函数不会使非友元函数具有友元状态。

    要解决这个问题,你可以写信Foo:

    std::unique_ptr<Spam> spam(new Spam(10));
    

    【讨论】:

    • 是的,你是对的。您能否在答案中添加更多详细信息?
    • 无法保证make_unique 不会委托给内部助手,在这种情况下,您的friend 将不起作用(更不用说签名也是错误的,因为make_unique 接受了它通过转发引用的参数)。
    • 一般来说,这种方法容易出现make_unique 应该解决的问题(即内存泄漏的可能性)
    【解决方案4】:

    在您的示例中,Foo()friend,但它不是创建 Spam 的函数 - make_unique 在内部调用 new Spam 本身。简单的解决方法是让Foo() 直接构造Spam

    void Foo() {
        std::unique_ptr<Spam> spam(new Spam(10));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2018-06-08
      • 2015-08-04
      • 2018-04-16
      • 1970-01-01
      相关资源
      最近更新 更多