c++ 成员变量似乎初始化了两次

yv5phkfx  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(163)

我有以下课程:
Bar.hpp

#include <string>

class Bar
{
public:
    Bar(const std::string& info);
    std::string getInfo() { return info; }

    protected:
    
    private:
        std::string info;

};

Bar.cpp:

#include <Bar.hpp>

Bar::Bar(const std::string& info)
        : info(info) { }

Foo.hpp:

#include <string>
#include <vector>

#include <Bar.hpp>

class Foo
{
    public:
        static void printInfoStore();
        static Foo* getFoo(const std::string& name);

        Foo() {};

        std::string getName() { return name; }
        std::vector<Bar> getBars();
        void addBar(const std::string& info);

    protected:

    private:
        static std::vector<Foo> infoStore;
        Foo(const std::string& name);
        std::vector<Bar> bars{};
        std::string name;

};

Foo.cpp

#include <iostream>

#include <Foo.hpp>

Foo::Foo(const std::string& name)
        : name(name) { }

//static
std::vector<Foo> Foo::infoStore(0);

//static
void Foo::printInfoStore() {
    std::cout << "InfoStore is { ";
    for (Foo& foo : infoStore) {
        std::cout << foo.getName() << "=[ ";
        for (Bar& bar : foo.getBars()) {
            std::cout << bar.getInfo() << " ";
        }
        std::cout << "] ";
    }
    std::cout << " }" << std::endl;
}

//static
Foo* Foo::getFoo(const std::string& name) {
    for (Foo& foo : infoStore) {
        if (foo.getName() == name) {
            return &foo;
        }
    }
    Foo* foo = new Foo(name);
    infoStore.push_back(*foo);
    return foo;
}

std::vector<Bar> Foo::getBars() {
    return bars;
}

void Foo::addBar(const std::string& info) {
    Bar* bar = new Bar(info);
    bars.push_back(*bar);
}

基本上,有一个静态向量保存多个Foo对象,每个对象都有一个Bar对象向量。
然后我有下面的主.cpp:

#include <Foo.hpp>
#include <Bar.hpp>

int main(int argc, char *argv[])
{
    Foo::printInfoStore();

    Foo::getFoo("Foo1")->addBar("info11");  // info11 is not added to Foo1
    Foo::printInfoStore();

    Foo::getFoo("Foo1")->addBar("info12");
    Foo::printInfoStore();

    Foo::getFoo("Foo1")->addBar("info13");
    Foo::printInfoStore();

    Foo::getFoo("Foo2")->addBar("info21");  // info21 is not added to Foo2
    Foo::printInfoStore();

    Foo::getFoo("Foo2")->addBar("info22");
    Foo::printInfoStore();

    return 0;
}

输出如下所示:

InfoStore is {  }
InfoStore is { Foo1=[ ]  }
InfoStore is { Foo1=[ info12 ]  }
InfoStore is { Foo1=[ info12 info13 ]  }
InfoStore is { Foo1=[ info12 info13 ] Foo2=[ ]  }
InfoStore is { Foo1=[ info12 info13 ] Foo2=[ info22 ]  }

奇怪的是,为每个Foo对象(info11info21)添加的第一个Bar没有被添加,我猜测在父Foo对象之后可能会发生bars向量的第二次初始化,但我不知道是否是这样,也找不到背后的原理。
我尝试在foo构造函数中显式初始化bars向量,但没有效果:添加的第一条总是被丢弃。
那么为什么会发生这种情况呢?我的代码有什么问题吗?可以做些什么来避免这种行为呢?

jqjz2hbq

jqjz2hbq1#

所有的new都导致内存泄漏。这些代码都不应该使用new,因为没有任何vector保存指针。
就此而言,由于getFoo()总是返回一个有效的对象,无论是现有的对象还是新推送的对象,它 * 应该 * 返回一个对该对象的Foo&引用,而不是一个Foo*指针(如果你真的想返回一个指针,你 * 可以 * 返回一个指针,但我不建议这样做)。
无论哪种方式,您都必须确保返回的内容实际上引用了vector内部的对象。您的输出不是您所期望的,因为代码没有正确执行此操作,这是问题的根源。
当你调用getFoo()来得到一个不存在的对象时,你创建了一个new对象,然后把这个对象的一个副本压入到你的vector中,然后返回一个指向艾德对象的指针,而不是一个指向复制对象的指针。当您稍后打印vector时,您随后存储在new艾德对象中的任何值看起来都被丢弃,因为vector内部的 copied 对象中不存在这些值。当你再次为一个 existing 对象调用getFoo()时,你会返回一个指向vector内部的 copied 对象的指针,并且不创建new对象。
类似地,getBars()也应该返回一个对它的vectorreference,而不是返回一个它的vectorcopy
说了这么多,试试更像这样的东西:
Bar.hpp

#include <string>

class Bar
{
public:
    Bar(const std::string& info);
    std::string getInfo() const;

private:
    std::string info;
};

Bar.cpp:

#include <Bar.hpp>

Bar::Bar(const std::string& info)
    : info(info) { }

std::string Bar::getInfo() const
    { return info; }

Foo.hpp:

#include <string>
#include <vector>

#include <Bar.hpp>

class Foo
{
public:
    static void printInfoStore();
    static Foo& getFoo(const std::string& name);

    std::string getName() const;

    std::vector<Bar>& getBars();
    const std::vector<Bar>& getBars() const;

    void addBar(const std::string& info);

private:
    static std::vector<Foo> infoStore;
    std::vector<Bar> bars;
    std::string name;

    Foo(const std::string& name);
};

Foo.cpp

#include <iostream>

#include <Foo.hpp>

Foo::Foo(const std::string& name)
    : name(name) { }

//static
std::vector<Foo> Foo::infoStore;

//static
void Foo::printInfoStore() {
    std::cout << "InfoStore is { ";
    for (const Foo& foo : infoStore) {
        std::cout << foo.getName() << "=[ ";
        for (const Bar& bar : foo.getBars()) {
            std::cout << bar.getInfo() << " ";
        }
        std::cout << "] ";
    }
    std::cout << " }" << std::endl;
}

//static
Foo& Foo::getFoo(const std::string& name) {
    for (Foo& foo : infoStore) {
        if (foo.getName() == name) {
            return foo;
        }
    }
    infoStore.push_back(Foo(name));
    return infoStore.back();

    // or, in C++11..14:
    // infoStore.emplace_back(name);
    // return infoStore.back();

    // or, in C++17 onward:
    // return infoStore.emplace_back(name);
}

std::string Foo::getName() const {
    return name;
}

std::vector<Bar>& Foo::getBars() {
    return bars;
}

const std::vector<Bar>& Foo::getBars() const {
    return bars;
}

void Foo::addBar(const std::string& info) {
    bars.push_back(Bar(info));

    // or, in C++11 onward:
    // bars.emplace_back(info);
}

main.cpp

#include <Foo.hpp>
#include <Bar.hpp>

int main(int argc, char *argv[])
{
    Foo::printInfoStore();

    Foo::getFoo("Foo1").addBar("info11");
    Foo::printInfoStore();

    Foo::getFoo("Foo1").addBar("info12");
    Foo::printInfoStore();

    Foo::getFoo("Foo1").addBar("info13");
    Foo::printInfoStore();

    Foo::getFoo("Foo2").addBar("info21");
    Foo::printInfoStore();

    Foo::getFoo("Foo2").addBar("info22");
    Foo::printInfoStore();

    return 0;
}
ifmq2ha2

ifmq2ha22#

将副本存储在向量中(有内存泄漏)。
固定版本:

Foo* Foo::getFoo(const std::string& name) {
    for (Foo& foo : infoStore) {
        if (foo.getName() == name) {
            return &foo;
        }
    }
    return &infoStore.emplace_back(Foo{name});
}

void Foo::addBar(const std::string& info) {
    bars.emplace_back(info);
}

Demo

相关问题