【问题标题】:Prevent temporary from extending its lifetime?防止临时延长其使用寿命?
【发布时间】:2011-11-26 08:47:45
【问题描述】:

这可能是不可能的,但我想知道是否有可能让一个暂时的东西永远不会超过它原来的表达方式。我有一个指向父对象的对象链,以及一个将创建子对象的成员函数,这里有一个简化的例子

class person{
    string name;
    person * mommy;
public:
    person(const string & nam, person * m = 0) : name(nam), mommy(m) {}
    person baby(const string & nam){
        return person(nam, this);
    }
    void talk() const{
        if (mommy) mommy->talk();
        cout << name << endl;
    }
};

int main(){
    person("Ann").baby("Susan").baby("Wendy").talk();     // fine

    const person & babygirl = person("Julie").baby("Laura"); // not fine

    babygirl.talk();    // segfault
    return 0;
}

我想使用person 的方式是将它传递给一个函数,如下所示:

void use(const person & p) {
    p.talk();
}
use(person("Anna").baby("Lisa"));

没问题。

只要没有一个临时对象在原始表达式之后仍然存在,这将正常工作,但是如果我将最终临时对象之一绑定到 const 引用,它的父对象将无法生存,并且我会遇到段错误。我可以隐藏person 的复制构造函数和赋值运算符,但是有什么办法可以防止这种错误的发生吗?如果可能,我想避免动态分配。

【问题讨论】:

  • 请注意,这段代码“不好”,就像写const int &amp;i = std::vector&lt;int&gt;(1)[0];“不好”一样。 vector 不会阻止你写,也不需要。这里的关键是,因为摧毁妈妈会使婴儿无法使用,所以婴儿是妈妈的一部分。这就是设计的问题,这是违反直觉的。您正在尝试通过防止出现任何诸如孤儿之类的东西来修补它,这可能是合适的,但您还应该考虑孤儿是否应该具有更好的定义行为,或者应该更明显地创建一个不好的东西。跨度>
  • 同意史蒂夫:这只是糟糕的设计展示。裸指针的出现应该表明有些东西已经关闭了。

标签: c++ reference object-lifetime const-reference temporaries


【解决方案1】:

看起来您在这里创建了一个数据结构,其中孩子有指向他们父母的指针。在这种情况下,使用临时工肯定会让您感到悲伤。为了确保安全,您需要动态分配并可能使用某种引用计数。

您是否考虑过使用boost::shared_ptr?它是引用计数智能指针类的实现。使用shared_ptr,或许还可以使用一些工厂方法,或许可以得到你想要的效果,减少动态内存分配的痛苦。我试过了,它似乎工作。一旦代码退出作用域,所有对象都将被销毁,因为没有对 shared_ptrs 的引用。

编辑: 作为对 zounds' 评论的回应,我修改了示例,以便根对象控制数据结构的生命周期。

#include <iostream>
#include <string>
#include <vector>
#include <boost\shared_ptr.hpp>
#include <boost\weak_ptr.hpp>

using boost::shared_ptr;
using boost::weak_ptr;

using std::string;
using std::cout;
using std::endl;
using std::vector;

class person;
typedef shared_ptr<person> Person;
typedef weak_ptr<person> PersonWk;

class person {    
    PersonWk pThis;
    friend Person makePerson(const string & nam, Person m = Person());

    string name;
    PersonWk mommy; // children should not affect parent lifetime, so store weak ptr
    vector<Person> children; // parents affect children lifetime so store child shared ptrs

    // make constructor private so that you can only make a person using factory method
    person(const string & nam, Person m) : name(nam), mommy(m) 
    { 
        // for demo purposes
        printf("creating %s\n", nam.c_str());
        ++personCount; 
    }

    // undefined copy constructor and assignment operators
    person(const person&);
    person& operator=(const person&);

public:
    // for demo purposes
    static int personCount;

    ~person() 
    { 
        // for demo purposes
        printf("destroying %s\n", name.c_str());
        --personCount; 
    }

    Person baby(const string & nam){        
        Person child = makePerson(nam, Person(pThis));
        children.push_back(child);
        return child;
    }

    void talk() const{
        if (Person mom = mommy.lock()) 
            mom->talk();
        cout << name << endl;
    }
};

int person::personCount = 0;

// factory method to make a person
Person makePerson(const string & name, Person m) {
    Person p = Person(new person(name, m));
    p->pThis = p; // stash weak_ptr so I can use it to make a shared_ptr from "this" in the baby method
    return p;
}

void use(const Person p) {
    printf("In method use...\n");
    p->talk();
}

int _tmain(int argc, _TCHAR* argv[])
{
    printf("personCount=%d\n", person::personCount);
    {
        Person ann = makePerson("Ann");

        // ann has baby and grandbaby, pass grandbaby to use method
        use(ann->baby("Susan")->baby("Wendy"));

        ann.reset(); // remove reference from root object. Destruction ensues...
    }
    printf("personCount=%d\n", person::personCount);
    return 0;
}

【讨论】:

  • 感谢您的回答。对我来说共享 ptr 的问题是根对象可能需要有一个特定的销毁点,我不想担心它可能在某处存储了指向它的指针。我只需要临时对象存在只要函数调用,然后它们就可以死掉。我正在使用一个 child->parent 结构,其中不允许孩子影响他们的父母(除非通过每个人都可以使用的普通公共界面)
  • 一种方法是将根对象存储在已知位置。然后让每个父母将 shared_ptrs 保留给他们的孩子,孩子们可以将 weak_ptrs 保留给他们的父母。然后当你将根对象设置为 NULL(从而删除所有引用)时,树就会从上到下被销毁。
  • 修改代码示例以使用我之前评论中的方法。
【解决方案2】:

你必须这样做:

void use(const person & p) {
    p.talk();
}
person a("Anna");
use(a.baby("Lisa"));

这样,父“a”不会超出范围,直到您真正完成它(在调用“use”之后)。

原始代码的问题是父“Anna”只需要停留足够长的时间就可以调用“baby”,并且可以在进行函数调用之前丢弃父级。通过将父级设置为具有范围的变量,您可以控制它何时被破坏。

这对我来说看起来很危险吗?是的。因此,我建议看一下 m-sharp 关于动态分配的答案。但是如果你想要一个不需要引用计数等的方法,那么你可以这样做......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-07
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    • 2014-07-04
    相关资源
    最近更新 更多