c ++中的auto_ptr问题

时间:2009-04-29 21:07:33

标签: c++

我是新来的。 我也是C ++的新手 所以这里是我写的类和函数。但是我得到了编译错误 我的班级:

class fooPlayer
{
public:
       void fooPlayerfunc(){}//doing something here
       char askYesNo(std::string question);
};

class fooPlayerFactory
{
public:
   virtual std::auto_ptr<fooPlayer> MakePlayerX() const;
   virtual std::auto_ptr<fooPlayer> MakePlayerO() const;
private:
   std::auto_ptr<fooPlayer> MakePlayer(char letter) const;
   std::auto_ptr<fooPlayer> my_player;

};

实施我的课程:

auto_ptr<fooPlayer> fooPlayerFactory:: MakePlayer(char letter) const
{
       my_player->fooPlayerfunc();
       return my_player;
}

auto_ptr<fooPlayer> fooPlayerFactory::MakePlayerX() const
{
      char go_first = my_player->askYesNo("Do you require the first move?");
      MakePlayer(go_first);
      return my_player;
}

auto_ptr<fooPlayer> fooPlayerFactory::MakePlayerO() const
{
    return my_player;
}

我的main()函数在这里:

int main()
{
          fooPlayerFactory factory;
          factory.MakePlayerX();
          factory.MakePlayerO();
}

我收到了错误: 错误C2558:类'std :: auto_ptr&lt; _Ty&gt;' :没有可用的复制构造函数或复制构造函数被声明为'explicit'

即使在阅读this链接上的文档后,我也不知道如何更改

6 个答案:

答案 0 :(得分:5)

错误的原因是你在fooPlayerFactory :: MakePlayerO()中调用auto_ptr my_player的复制构造函数,这是一个const方法。这意味着无法修改其成员。

然而,auto_ptr的复制构造函数会修改右侧,因此返回my_player trys将其指针更改为0(NULL),同时在返回值中将原始指针指定给auto_ptr。

复制构造的签名是

auto_ptr<T>::auto_ptr<T>(auto_ptr<T> & rhs)

不是

auto_ptr<T>::auto_ptr<T>(const auto_ptr<T> & rhs)

auto_ptr的复制构造函数将指针的所有权分配给左侧,右侧则不保留任何内容。

我认为你不想在这里使用auto_ptr,你可能想要boost :: smart_ptr

看起来你混淆了auto_ptr的两个用途

第一个是穷人的提升:: scoped_ptr。这是为了管理类中指针的单个实例,该类管理指针的生命周期。在这种情况下,你通常不会在你的类之外返回这个指针(你可以这是合法的,但是boost :: smart_ptr / boost :: weak_ptr会更好,所以客户可以参与指针的生命周期)

第二个是它的主要目的是以异常安全的方式将新创建的指针返回给函数的调用者。

例如

auto_ptr<T> foo() {
    return new T;
}

void bar() {
    auto_ptr<T> t = foo();
}

正如我所说,我认为你混合了这两个用法auto_ptr是一个微妙的野兽,你应该仔细阅读auto_ptr文档。它在Effective STL by Scott Meyers中也很清楚。

答案 1 :(得分:2)

在您的代码中:

auto_ptr<fooPlayer> fooPlayerFactory:: MakePlayer(char letter) const
{
       my_player->fooPlayerfunc();
       return my_player;
}

这是一个const函数,但fooPlayerfunc不是常量 - 我的编译器会报告此错误,而不是您说的那个错误。你发布真实的代码吗?

答案 2 :(得分:2)

我认为你真的不想在这里构建动态对象 工厂对象创建并返回一个对象,它通常在创建后不会保留对它的引用(除非你正在共享它),而我实际上并没有看到你在创建播放器的任何地方。

如果你只在你的(fooPlayerFactory)内部创建一个玩家。然后创建一个对象并返回对它的引用。

答案 3 :(得分:1)

编辑:为了回应评论(这是正确的,我的坏),我只留下了建议部分。

最佳做法是让工厂方法只返回一个指向底层对象的普通旧指针,然后让调用者决定如何管理所有权(auto_ptr,scoped_ptr或其他)。

答案 4 :(得分:0)

我没有看到你构建my_player的任何地方,所以我感觉有些代码丢失了。具体来说,我认为你的构造函数有这一行:

my_player = new fooPlayer()

fooPlayer对象与auto_ptr<fooPlayer>对象不完全相同,auto_ptr是故意设计的,以防止从一个对象分配到另一个对象,因为坦白说,替代方案更糟糕。有关详细信息,请查找(1)转换构造函数,(2)explicit关键字,以及(3)复制构造函数和破坏性复制语义。

您应该将构造函数更改为:

class fooPlayerFactory {
    public:
    fooPlayerFactory()
    {
        my_player = std::auto_ptr<fooPlayer>(new fooPlayer());
    }

或(使用成员初始化列表):

class fooPlayerFactory {
    public:
    fooPlayerFactory() : my_player(std::auto_ptr<fooPlayer>(new fooPlayer()) { }

解决方案并不漂亮,但正如我所说,由于一些非常神秘的细节,替代方案更糟糕。


但是,作为一点建议,你的生活比现在更难;事实上可能会导致奇怪的错误。存在auto_ptr来管理对象的生命周期,但是您需要担心my_player的生命周期的唯一原因是您已使用new分配了它。但是没有必要致电new,事实上没有必要保留my_player。除非fooPlayerFactory是其他工厂的基类,否则无需标记函数virtual

最初我认为你只需返回my_player对象的副本就可以逃脱,但是有一个问题:在从my_player返回MakePlayer()之前你会调用一个方法,然后我假设该方法更改my_player的内部状态。对MakePlayer()的进一步调用将再次改变状态,我认为你最终将my_player置于错误的状态。相反,每个请求都返回一个不同的fooPlayer对象。不要做内存管理,只是承诺构造对象。这样用户可以决定内存分配:

fooPlayerFaclotry factory;
fooPlayer on_stack = factory.MakePlayerX();
fooPlayer* on_heap_raw_pointer = new fooPlayer(factory.MakePlayerO());
std::auto_ptr<fooPlayer> on_heap_managed_scope
                         = std::auto_ptr<fooPlayer>(factory.MakePlayerX());

我会将fooPlayerFactory更改为:

class fooPlayerFactory
{
private:
   fooPlayer MakePlayer(const char letter) const
   {
       fooPlayer result;
       result.fooPlayerfunc();
       return result;
   }

public:
   fooPlayer* MakePlayerX() const
   {
       char go_first = askYesNo("Do you require the first move?");
       return MakePlayer(go_first);
   }

   fooPlayer MakePlayerO() const
   {
       return fooPlayer();
   }
};

答案 5 :(得分:0)

此外,您的代码是错误的,任何实现虚方法的类都应该有一个虚拟析构函数。