关于设置类变量的C ++问题

时间:2009-03-13 16:38:36

标签: c++

我不是编程的新手,但是在使用Java之后我又回到了C ++,并且对于不是指针的类变量感到有些困惑。给出以下代码:

#include <iostream>
#include <map>

using namespace std;

class Foo {
    public:
        Foo() {
            bars[0]     = new Bar;
            bars[0]->id = 5;
        }

        ~Foo() { }

        struct Bar {
            int id;
        };

        void set_bars(map<int,Bar*>& b) {
            bars = b;
        }

        void hello() {
            cout << bars[0]->id << endl;
        }

    protected:
        map<int,Bar*> bars;
};

int main() {
    Foo foo;
    foo.hello();

    map<int,Foo::Bar*> testbars;
    testbars[0]     = new Foo::Bar;
    testbars[0]->id = 10;

    foo.set_bars(testbars);

    foo.hello();

    return(0);
}

我得到的预期输出为5&amp; 10.但是,我对C ++中的引用和指针缺乏了解,这让我想知道这实际上是否会在野外工作,或者如果一旦testbars超出范围就会barf。当然,在这里,testbars在程序结束之前不会超出范围,但如果它是在另一个类函数中创建的函数变量怎么办?无论如何,我想我的主要问题是,将条形变量创建为指向地图的指针会更好/更安全吗?

5 个答案:

答案 0 :(得分:4)

  

无论如何,我想我的主要问题是   我创造它会更好/更安全吗?   bars类变量作为指针   到地图?

没有。 C ++在这方面与Java完全不同,可能还有其他方面。如果你发现自己使用指针并为它们分配新的对象,你可能做错了什么。为了学习正确的做事方式,我建议您获取Koenig&amp;撰写的Accelerated C++副本。 MOO,

答案 1 :(得分:3)

成员变量bars是“类字典”/关联数组类的单独实例。因此,当它在set_bars中分配时,参数b的内容将被复制到bars中。因此,无需担心footestbars的相对生命周期,因为它们是独立的“类似价值”的实体。

Bar对象的生命周期存在更多问题,目前这些对象永远不会被删除。如果您在某处添加代码以删除它们,那么您将引入另一个问题,因为您正在复制Bar对象的地址(而不是对象本身),因此您有两个不同地图指向的相同对象。删除对象后,另一个地图将继续引用它。这是你应该像C ++中的瘟疫一样避免的事情!分配给new的对象的裸指针是等待发生的灾难。

引用(用&amp;声明)与关于对象生存期的指针没有区别。为了允许您从两个地方引用同一个对象,您可以使用指针或引用,但这仍然会给您带来解除分配的问题。

您可以通过使用类似shared_ptr的类来解决解除分配问题,这应该包含在任何最新的C ++环境中(std::tr1中)。但是你可能遇到周期性指针网络的问题(例如,A指向B和B指向A),不会自动清理。

答案 2 :(得分:1)

对于每个新的你需要相应的删除。 如果你在调用delete之后尝试引用内存 - 那就是 - 那么程序确实会“barf”。

如果你不那么你会没事的就是这么简单。

您应该设计您的类,以便内存的所有权是明确的,并且您知道对于您正在进行相同释放的每个分配。 永远不要假设另一个类/容器会删除你分配的内存。

希望这有帮助。

答案 3 :(得分:0)

在下面的代码中,您可以传递Bars的地图,然后就可以修改该类之外的Bars。

但是。但除非你再次调用set_bars。

当一个对象负责创建和删除Bars时更好。在你的情况下,这不是真的。

如果你想要,你可以使用boost :: shared_ptr&lt;酒吧&gt;而不是酒吧*。这将是更像Java的行为。

class Foo {
public:
    Foo() {
        bars[0]     = new Bar;
        bars[0]->id = 5;
    }

    ~Foo() { freeBarsMemory(); }

    struct Bar {
        int id;
    };

    typedef std::map<int,Bar*> BarsList;

    void set_bars(const BarsList& b) {
        freeBarsMemory();
        bars = b;
    }

    void hello() {
        std::cout << bars[0]->id << std::endl;
    }

protected:
    BarsList bars;

    void freeBarsMemory()
    {
        BarsList::const_iterator it = bars.begin();
        BarsList::const_iterator end = bars.end();

        for (; it != end; ++it)
            delete it->second;

        bars.clear();
    }
};

答案 4 :(得分:0)

  

我不是编程的新手,但是在使用Java之后我又回到了C ++,对于那些不是指针的类变量感到有些困惑。

混淆似乎来自堆上的数据和不一定在堆上的数据的组合。这是造成混淆的常见原因。

在您发布的代码中,bars不是指针。因为它在类范围内,所以它将一直存在,直到包含它的对象(testbars)被销毁。在这种情况下,testbars在堆栈上创建,因此当它超出范围时将被销毁,无论该范围的嵌套程度如何。当testbars被销毁时,testbars的子对象(无论它们是父类还是testbars对象中包含的对象)将使它们的析构函数以明确定义的顺序在该精确时刻运行

这是C ++的一个非常强大的方面。想象一个具有10行构造函数的类,它打开网络连接,在堆上分配内存,并将数据写入文件。想象一下,类的析构函数撤消所有这些(关闭网络连接,释放堆上的内存,关闭文件等)。现在想象一下,在构造函数的中途创建此类的对象失败(例如,网络连接已关闭)。程序如何知道析构函数的哪些行将撤消成功构造函数的各个部分?没有一般方法可以知道这一点,因此该对象的析构函数不会运行。

现在想象一个包含十个对象的类,每个对象的构造函数都会做一件必须回滚的事情(打开网络连接,在堆上分配内存,将数据写入文件等)和每个对象的析构函数包括回滚操作所需的代码(关闭网络连接,释放对象,关闭文件等)。如果只成功创建了五个对象,则只需要销毁这五个对象,并且它们的析构函数在该确切时刻运行。

如果已在堆上创建testbars(通过new),那么只有在调用delete时才会销毁它。通常,在堆栈上使用对象要容易得多,除非有一些理由使对象比它创建的范围更长。

带我到Foo::barFoo::bars是引用堆上对象的map。好吧,它指的是在这个代码示例中引用堆上分配的对象的指针(指针也可以引用堆栈上分配的对象)。在示例中,您发布了这些指针所引用的对象永远不会delete d,并且因为这些对象在堆上,所以会出现(小)内存泄漏(操作系统在程序退出时清除)。根据STL,std::mapsFoo::bar执行 delete指针,它们在被销毁时引用。 Boost有一些解决这个问题的方法。在你的情况下,简单地不在堆上分配这些对象可能是最容易的:

#include <iostream>
#include <map>

using std::map;
using std::cout;

class Foo {
    public:
        Foo() {
            // normally you wouldn't use the parenthesis on the next line
            // but we're creating an object without a name, so we need them
            bars[0] = Bar();
            bars[0].id = 5;
        }

        ~Foo() { }

        struct Bar {
            int id;
        };

        void set_bars(map<int,Bar>& b) {
            bars = b;
        }

        void hello() {
            cout << bars[0].id << endl;
        }

    protected:
        map<int,Bar> bars;
};

int main() {
    Foo foo;
    foo.hello();

    map<int,Foo::Bar> testbars;
    // create another nameless object
    testbars[0] = Foo::Bar();
    testbars[0].id = 10;

    foo.set_bars(testbars);
    foo.hello();
    return 0;
}