【问题标题】:c++ taking address of temporaryc ++获取临时地址
【发布时间】:2011-03-21 08:56:31
【问题描述】:

我有简单的类方法,比如

Task TaskSample::Create(void)
{
    Task task;
    return task;
}

并收到临时地址的警告。这段代码有问题吗? 我不想在这里使用指针

【问题讨论】:

  • 使用类时,返回类对象并不是很有效(尤其是在任务类/结构很大的情况下)。
  • @Phong:我最近发现 NRVO(命名返回值优化)会起作用。该标准允许在从方法返回类实例时绕过类的复制构造函数的优化。跨度>
  • @Phong:根据上下文,not 返回的效率可能会降低,因为您会强制客户端在调用函数之前默认构造您的对象。
  • @zigi,@Billy ONeil 的答案是正确的,您提交的代码没有任何问题。您需要向我们提供更多详细信息。您收到的错误消息到底是什么?究竟是哪一行代码发出了警告?您是否在代码中调用此方法,例如 "Task &t = TaskSample::Create()" ?
  • 不,您没有从该代码中得到类似的警告。您的代码是假的,您对警告的描述是假的。发布您收到的真实代码或真实警告。

标签: c++ warnings


【解决方案1】:

如果这实际上是您的代码,那么编译器可能有错误。

然而,更有可能的是,您实际上是这样写的:

Task& TaskSample::Create(void)
{
    Task task;
    return task;
}

删除 & 以按值返回,而不是按引用返回。通过引用返回没有意义,因为task 将在函数返回时被销毁。

【讨论】:

    【解决方案2】:

    以下两个代码 sn-ps 在 MS C++ 中都会产生此错误:

    警告 C4172:返回地址 局部变量或临时变量

    Task* Create(void)
    {
        Task task;
        return &task;
    }
    Task& Create2(void)
    {
        Task task;
        return task;
    }
    

    MSDN documentation 非常简洁地描述了警告:

    局部变量和临时对象 当函数返回时被销毁, 所以返回的地址是无效的。

    为了返回指向对象的指针,您需要调用operator new,因为在堆上分配的对象不会超出范围:

    Task* Create(void)
    {
        Task* task = new Task();
        return task;
    }
    

    完成后不要忘记delete该任务:

    Task* task = Create();
    delete task;
    

    您也可以使用smart pointer:

    void Test () {
      boost::scoped_ptr<Task> spTask = Create();
      spTask->Schedule(); 
    } //<--- spTask is deleted here
    

    我会改为依赖 RVO 并实际使用您发布的代码,而这很可能不是给您警告的代码。

    void Test() {
       Task task = Create();
    }
    
    Task Create(void)
    {
        Task task;
        task.start = 10;
        return task;
    }
    

    这可能会生成与此等效的东西,因此实际上没有复制构造函数开销。

    void Test() {
       Task task;
       Create(&task);
    }
    
    Task* Create(Task* __compilerGeneratedParam)
    {
        __compilerGeneratedParam->start = 10;
        return __compilerGeneratedParam;
    }   
    

    【讨论】:

      【解决方案3】:

      现代编译器可以优化返回值以避免复制开销。通常按价值返回根本不会影响性能。

      但如果需要通过引用返回,请改用 shared_ptr。

      shared_ptr<Task> TaskSample::Create(void)
      {
          shared_ptr<Task> ptr(new Task(...));
          return ptr;
      }
      

      【讨论】:

      • 如果您选择返回动态分配的内存,auto_ptr [或者可能unique_ptr 如果您可以访问 C++0x] 在这里可能更合适。
      • auto_ptr 具有移动语义,容易误用。默认的通用智能指针应该是 shared_ptr,因为它的行为类似于普通指针,而且它会为您进行内存释放。其他智能指针仅在特殊情况下有用,不应被视为普通指针的替代品。
      • @m141:实际上,如果unique_ptr 可用,它是更好的选择。但是,它仅在 C++0x 上可用。您可以获得 shared_ptr 的大部分好处,但您可以更简单地定义对象的所有权。
      • @rn141:这里需要移动语义。所有智能指针的行为都类似于指针。所有智能指针都会进行内存释放。普通指针应该是绝对的最后选择,因为它们不是异常安全的(并且很可能被错误地使用),并且只有当所有权被明确定义为属于其他地方时。这是 auto_ptr 的完美案例(或在较新版本的 c++ unique_ptr
      • 我们使用指针是为了避免复制繁重的对象或共享所有权。如果所有权仅限于特定范围,那么 scoped_ptr 可能更合适(请不要理会 unique_ptr 直到它成为标准)当它起作用时,elide 优化比任何智能指针都要好得多。 shared_ptr 可以在 auto_ptr 不能工作的地方工作。有很多关于 auto_ptr 搞砸的在线博客和文章。 Herb Sutter 发表了几篇关于 auto_ptr 问题的 GotWs。这些天来,我在工业 C++ 代码中没有任何 auto_ptr。 99% 的智能指针是 shared_ptr,其余的是 scoped_ptr。
      【解决方案4】:

      最好是通过引用传递对象,如下所示

      void TaskSample::Create(Task& data)
      {
      
      ....
      
      }
      

      【讨论】:

      • -1:这可能曾经是正确的,但现在你最好做一些符合概念的事情并返回对象。如有必要,编译器会优化副本,结果是更好的代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      相关资源
      最近更新 更多