联系人数据结构应定义在独立的contact.h头文件中,仅包含contact类及其纯数据操作,不掺杂管理逻辑、业务校验或io功能,确保职责单一与低耦合。

联系人数据结构该定义在哪个头文件里
联系人信息必须独立成类(或结构体),且只管自身字段和基础操作,不掺杂任何管理逻辑。常见错误是把 Contact 类塞进 AddressBook.h 里,结果导致头文件耦合、编译依赖爆炸。
正确做法是新建 Contact.h,仅包含:
class Contact {
public:
std::string name;
std::string phone;
std::string email;
// 只提供构造、打印、序列化等纯数据操作
void print() const;
std::string to_string() const;
};
- 所有成员变量设为
public或用get_*方法暴露——别加一堆业务相关的validate_phone()这类函数 - 不要在
Contact里持有索引、ID 或指向通讯录的指针,它不该知道自己被谁管理 - 如果要用
std::vector<contact></contact>存储,那是管理模块的事,不是Contact的责任
通讯录管理类怎么设计接口才不越界
管理类(比如 AddressBook)只负责增删查改容器、持久化、排序等“容器级”操作,绝不能侵入联系人语义。典型越界行为:在 AddressBook::add() 里做手机号格式校验、自动补区号、去重合并逻辑。
推荐接口签名保持窄而明确:
class AddressBook {
public:
void add(const Contact& c);
bool remove(const std::string& name); // 仅按姓名删(单一维度)
std::vector<contact> search(const std::string& keyword) const;
void save_to_file(const std::string& path) const;
void load_from_file(const std::string& path);
private:
std::vector<contact> contacts_;
};</contact></contact>
- 搜索返回
std::vector<contact></contact>,而不是int索引或指针——避免暴露内部存储细节 - 所有修改操作(
add/remove)不返回状态码,失败时抛std::runtime_error(例如文件打不开、重复添加等非业务错误) - 不提供
get_contact_by_index(int i)这类下标访问——外部应通过搜索或遍历获取,否则管理类成了裸容器包装器
main.cpp 里怎么组织调用链才不乱
入口文件只做三件事:解析用户输入、调用管理类接口、输出结果。绝不出现 Contact c; c.name = "xxx"; 这种直接构造和赋值——那属于测试或初始化逻辑,应抽到单独函数里。
- 用户输入解析建议用
std::map<:string std::function>></:string>映射命令字符串到操作函数,避免大段if-else - 每条命令对应一个独立函数(如
handle_add(AddressBook& ab)),里面只做输入读取 + 调用ab.add(...)+ 异常捕获 - 禁止在
main()里直接操作contacts_成员——哪怕只是ab.contacts_.size()也不行;所有状态查询走ab.size()这类封装接口(需在AddressBook中补充)
为什么 operator
流输出是展示层逻辑,和数据模型无关。把 operator 塞进 <code>Contact 头文件,等于强制所有包含它的代码都依赖 <iostream></iostream>,破坏可移植性(比如将来想把 Contact 用在嵌入式无 IO 环境)。
正确位置是单独的 io_utils.h:
// io_utils.h #include <iostream> #include "Contact.h" std::ostream& operator <ul> <li>这样 <code>Contact.h</code> 保持干净,不引入任何 IO 头</li> <li>如果需要不同格式输出(JSON / CSV),只需新增 <code>to_json_string(const Contact&)</code> 等函数,不影响原有结构</li> <li>单元测试时也方便 mock 输出行为——你测的是数据,不是怎么打印它</li> </ul> 拆分的关键不在文件数量,而在职责是否真正隔离:Contact 不知道 AddressBook 存在哪,AddressBook 不关心 name 字段要不要正则校验,main 不碰任何数据成员。一旦发现某个函数同时在处理“手机号合法性”和“写入文件”,就说明边界已经破了。</iostream>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











