【问题标题】:Public new private constructor公共新私有构造函数
【发布时间】:2010-02-01 17:41:42
【问题描述】:

当我尝试编译以下内容时:

#include <iostream>

class Test
{
public:
    void* operator new (size_t num);
    void operator delete (void* test);
    ~Test();
private:
    Test();
};

Test::Test()
{
    std::cout << "Constructing Test" << std::endl;
}

Test::~Test()
{
    std::cout << "Destroying Test" << std::endl;
}

void* Test::operator new (size_t num)
{
    ::new Test;
}

void Test::operator delete(void* test)
{
    ::delete(static_cast<Test*>(test));
}

int main()
{
    Test* test = new Test;
    delete test;
}

我明白了:

$ g++ -o test test.cpp
test.cpp: In function ‘int main()’:
test.cpp:14: error: ‘Test::Test()’ is private
test.cpp:36: error: within this context

如果new是成员函数,为什么不能调用私有构造函数?

编辑: 我的想法是创建一个只能使用完全标准语法在堆上实例化的类。我希望由于 new 是一个数据成员,它可以调用私有构造函数,但是由于 new 不用于堆栈对象,因此不允许您在堆栈上创建对象。

【问题讨论】:

  • 不要乱用 html 格式为您的代码,只需使用“代码格式”按钮,或使用 4 个空格缩进代码块。
  • 我试过了,只是剪切和粘贴没有成功。
  • 只是好奇,是什么用例导致您阻止给定类型的堆栈分配?

标签: c++


【解决方案1】:

我认为您对operator new 的作用存在误解。它不创建对象,而是为对象分配内存。编译器将在调用你的 operator new 之后立即调用构造函数。

struct test {
   void * operator new( std::size_t size );
};
int main()
{
   test *p = new test;
   // compiler will translate this into:
   //
   // test *p = test::operator new( sizeof(test) );
   // new (static_cast<void*>(p)) test() !!! the constructor is private in this scope
}

操作符 new 的主要用途是拥有一个不同于系统默认分配器(通常是 malloc)的内存分配器,它意味着返回一个未初始化的内存区域,编译器将在该区域上调用构造函数。但是构造函数是在 之后调用的,内存是在写入新调用的范围内分配的(在本例中为 main)。

验收后说明

未制定问题的完整解决方案:如何强制我的类的用户在堆中实例化? 将构造函数设为私有并提供工厂函数,如其他一些答案所示(正如villintehaspam 所指出的)。

【讨论】:

  • 有意义,因为 new 返回一个 void*(即未构造的内存)
【解决方案2】:

这是强制在堆上创建对象的方法:

class Foo {
public:
    static Foo *Create() {
         return new Foo;
    }
private:
    Foo() {}
};

然后当你使用它时:

Foo *foo = Foo::Create();

您可能需要考虑返回 shared_ptr 而不是原始指针,以帮助确保对象被删除。

从技术上讲,这不是您所要求的,但这是您表示您希望实现的...

【讨论】:

    【解决方案3】:

    new 不会调用构造函数 - 编译器会调用,并且构造函数必须可供它访问。使用这个更简单的代码:

    class A {
        A() {}
    public:
        void * operator new( size_t x ) { return 0; }
    };
    
    int main() {
        A* a = new A;
    }
    

    很明显,new 并没有调用构造函数,但是如果你编译它,你仍然会得到一个“私有构造函数”的错误信息..

    【讨论】:

    • 那么,这是否说明了为什么编译器不能调用私有构造函数?
    • 这不是问题所在。但是,编译器当然不能调用私有构造函数——因此将复制构造函数设为私有以防止按值调用。
    【解决方案4】:

    由于各种各样的人错误地概述了你正在做的事情不起作用的原因;我通常实现你的“不在堆栈上”目标的方式就是这样

    class HeapOnly {
      public:
        static HeapOnly* CreateInstance() { return new HeapOnly(); }
      protected:
        HeapOnly() { }
    };
    

    现在实例化它的唯一方法是: HeapOnly *MyObj=HeapOnly::CreateInstance();

    【讨论】:

      【解决方案5】:

      问题是如果构造函数是私有的,你就不能实例化一个对象。

      为什么将构造函数设为私有?

      【讨论】:

      • 我想要一个只能在堆上实例化的对象。我希望由于 new 运算符是类的一部分,它能够调用私有构造函数。
      • 查看“Named Constructor Idiom”...您只需创建一个公共静态“Create...”方法,该方法可以调用您的私有构造函数并仅在堆上创建实例。
      • 它是,问题是你的类之外的范围 - 这是它被实例化的地方 - 不能调用构造函数,因为它是私有的。既然你只想在堆上构建,那么写一个 Create() 或 Clone() 方法怎么样。或者,如何使用智能指针。
      猜你喜欢
      • 2011-02-08
      • 2017-04-30
      • 2016-12-03
      • 2010-10-13
      • 1970-01-01
      • 2019-08-24
      • 2016-07-31
      • 1970-01-01
      • 2016-01-09
      相关资源
      最近更新 更多