You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++类成员变量疑似重复初始化?首次添加的Bar对象丢失问题

Foo实例首次添加Bar丢失的原因与修复方案

问题描述

实现了Bar和Foo两个C++类:

  • Bar类包含字符串成员info
  • Foo类通过静态vector<Foo> infoStore存储实例,每个Foo实例持有vector<Bar> bars

调用时发现每个Foo对象首次添加的Bar(如info11、info21)并未被保存,后续添加的Bar则正常,即使在构造函数显式初始化bars也无法解决该问题。

相关代码

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);
}

main.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 ]  }

原因分析

核心问题出在getFoo函数的逻辑:

  1. 创建新Foo实例时,先通过new Foo(name)在堆上分配一个对象
  2. 调用infoStore.push_back(*foo)时,会将堆上Foo对象的副本存入vector
  3. 函数返回的是堆上Foo对象的指针,而非infoStore中副本的指针

首次调用addBar是给堆上的Foo对象添加Bar,但这个堆对象并没有被存入infoStore;第二次调用getFoo时,会找到infoStore里的副本并返回其指针,后续的addBar都是操作这个副本,所以首次添加的Bar不会出现在infoStore的实例中,也就不会被打印出来。

代码存在的问题

  • getFoo逻辑错误:堆对象与infoStore中的实例是两个独立对象,导致首次修改的是未被存储的堆对象
  • getBars返回值为值拷贝:每次调用都会复制整个bars向量,既浪费性能,也无法通过返回值修改原对象的bars
  • addBar存在内存泄漏:通过new创建Bar对象后未delete,导致内存无法回收
  • 无参构造函数为public权限:可能意外创建未初始化name的无效Foo实例

修复方案

1. 修正getFoo函数逻辑

直接在infoStore中构造Foo实例,避免副本问题:

Foo* Foo::getFoo(const std::string& name) {
    for (Foo& foo : infoStore) {
        if (foo.getName() == name) {
            return &foo;
        }
    }
    // 直接在vector末尾构造Foo实例,避免拷贝
    infoStore.emplace_back(name);
    // 返回vector中最后一个元素的指针
    return &infoStore.back();
}

2. 修改getBars返回引用

避免不必要的拷贝,同时保证访问的是原对象的bars:
在Foo.hpp中修改声明:

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

在Foo.cpp中修改实现:

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

3. 修复addBar的内存泄漏

直接在bars中构造Bar对象,无需堆分配:

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

4. 限制无参构造函数权限

将无参构造函数设为private,防止意外创建无效实例:

class Foo
{
    // ... 其他成员 ...
private:
    Foo() {};
    // ... 其他私有成员 ...
};

修复后程序输出

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

内容的提问来源于stack exchange,提问作者Overflow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:20:25