C++中Obj与Obj*的区别及适用场景,Person映射容器选型咨询
在C++中
Obj 与 Obj* 的区别及使用场景,附你的Person结构体分析 嘿,我来帮你理清这个C++里的核心问题,先从Obj和Obj*的本质区别说起,再结合你的Person结构体和map的场景具体分析~
一、Obj 和 Obj* 的核心区别
Obj是实实在在的对象本身:它是值类型,存储在栈(局部变量)或分配的内存块(类成员)里。创建时直接调用构造函数初始化整个对象,赋值时是深拷贝(除非你自定义了移动/拷贝逻辑),销毁时自动调用析构函数,不用你操心内存。Obj*只是个“地址标签”:它是指针类型,本身只占4/8字节(取决于系统位数),存储的是某个Obj对象的内存地址。赋值时只是拷贝地址,不会复制对象本身;销毁指针的时候,不会自动销毁它指向的对象(裸指针的话,你得手动delete,不然会内存泄漏)。
二、怎么判断该用哪一种?
优先选 Obj 的情况
- 对象生命周期和当前作用域绑定,不需要在多个地方共享,比如临时变量、简单的类成员。
- 想避免指针的空指针、野指针风险,或者希望自动管理对象生命周期的时候。
- 对象体积小,拷贝成本低,值传递/存储更简单直观。
必须用 Obj*(或智能指针)的情况
- 需要共享对象:比如你的
Person结构体里,父母和孩子要指向同一个人,用指针才能让多个对象操作同一个实例,避免拷贝导致的数据不一致。 - 动态控制生命周期:比如对象需要在堆上创建,生命周期超过当前作用域(比如要在函数外继续使用)。
- 处理多态:如果
Obj是基类,要指向派生类对象,必须用指针(或引用)才能触发多态调用。 - 对象太大拷贝成本高,或者不可拷贝:比如含有
std::unique_ptr这类不可拷贝成员的对象,只能用指针传递/存储。
三、针对你的Person结构体和Personmap的选择
先看你的结构体:
struct Person { std::string id_ = NO_ID; int height_ = NO_HEIGHT; std::vector<Person*> parents_{nullptr, nullptr}; std::vector<Person*> children_; }; using Personmap = std::map<std::string, Person>;
核心问题:你的Person有双向引用关系(父母↔孩子),存储Person值类型会踩坑!
如果map存的是Person(值),会出现这些问题:
- 引用失效:map内部会自动排序/重新平衡,当你插入新元素时,已有的
Person对象可能会被移动内存位置,这时候其他Person里指向它的指针就变成了野指针,访问会直接崩溃或出现未定义行为。 - 数据不一致:每次你从map里取
Person,拿到的是拷贝,修改这个拷贝不会影响原对象;而且每个拷贝的parents_和children_都是独立的,根本构建不出正确的家族关系。
正确的选择:存储智能指针(优先推荐 std::shared_ptr)
把Personmap改成std::map<std::string, std::shared_ptr<Person>>,原因如下:
- 自动管理内存:
shared_ptr会记录引用次数,当没有任何指针指向对象时,自动调用delete,避免内存泄漏。 - 保证引用有效:只要
shared_ptr存在,对象的内存就不会被释放,其他指针(或shared_ptr)指向它时永远有效。 - 解决循环引用:因为父母和孩子互相指向,用
shared_ptr会导致循环引用(计数永远不为0,对象无法销毁),这时候可以把其中一方改成std::weak_ptr,比如孩子的parents_用weak_ptr<Person>,既能访问父母对象,又不会增加引用计数。
举个简单的实现例子:
#include <memory> #include <map> #include <string> #include <vector> const std::string NO_ID = ""; const int NO_HEIGHT = 0; struct Person { std::string id_ = NO_ID; int height_ = NO_HEIGHT; std::vector<std::weak_ptr<Person>> parents_; // 用weak_ptr避免循环引用 std::vector<std::shared_ptr<Person>> children_; }; using Personmap = std::map<std::string, std::shared_ptr<Person>>; int main() { Personmap people; // 创建Alice并加入map auto alice = std::make_shared<Person>(); alice->id_ = "Alice"; alice->height_ = 165; people["Alice"] = alice; // 创建Bob,把Alice设为父母 auto bob = std::make_shared<Person>(); bob->id_ = "Bob"; bob->height_ = 180; bob->parents_.push_back(alice); // weak_ptr可以从shared_ptr直接构造 people["Bob"] = bob; // 把Bob加入Alice的孩子列表 alice->children_.push_back(bob); }
为什么不推荐裸指针Person*?
裸指针需要你手动new创建、delete销毁,很容易遗漏delete导致内存泄漏,或者在对象被销毁后还有指针指向它(野指针)。智能指针能帮你自动处理这些细节,代码更安全、更易维护。
内容的提问来源于stack exchange,提问作者Grak
相关产品推荐
相关产品推荐

