C++构造/析构函数调用顺序疑问:PhoneBook与Contact类实例解析
问题:PhoneBook中Contact对象的构造/析构日志与预期不符
问题背景
定义了PhoneBook类和Contact类,期望PhoneBook的构造函数实例化一个包含8个Contact对象的数组。代码可正常运行,但打印构造/析构函数的调用日志后,结果与预期不符。改用动态分配(将_contacts声明为指针并使用new/delete[])后,日志符合预期,需要解释其中原因。
代码实现
main函数
int main(void) { PhoneBook phonebook; //.. return (0); }
Contact类定义与实现
class Contact { public: Contact(); // construct a Contact ~Contact(); // destruct a Contact void init(void); // init a Contact void preview(int) const; // preview a Contact void print(int) const; // print a Contact void setIndex(int); // set index private: std::string _first_name; std::string _last_name; std::string _nickname; std::string _phone_number; std::string _darkest_secret; int _index; std::string _printLen(std::string str) const; std::string _getInput(std::string str) const; }; Contact::Contact() { std::cout << "Contact created" << std::endl << std::flush; } Contact::~Contact() { std::cout << "Contact destroyed" << std::endl << std::flush; }
PhoneBook类定义与实现
class PhoneBook { public: PhoneBook(); // construct a PhoneBook ~PhoneBook(); // destroy a PhoneBook std::string welcome(void) const; void addEntry(void); // add a new contact void previewContacts(void) const; void searchEntry(void) const; private: Contact _contacts[8]; // 原代码中的数组声明 int _size; // number of contacts }; PhoneBook::PhoneBook(): _size(0) { std::cout << "PhoneBook created" << std::endl << std::flush; for (int i = 0; i < 8; i++) _contacts[i] = Contact(); } PhoneBook::~PhoneBook() { std::cout << "PhoneBook destroyed" << std::endl << std::flush; }
实际输出
Contact created Contact created Contact created Contact created Contact created Contact created Contact created Contact created PhoneBook created Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed Contact created Contact destroyed
预期输出
PhoneBook created Contact created Contact created Contact created Contact created Contact created Contact created Contact created Contact created Contact destroyed Contact destroyed Contact destroyed Contact destroyed Contact destroyed Contact destroyed Contact destroyed Contact destroyed PhoneBook destroyed
原因解释
数组版本(原代码)的问题
- 数组元素的默认构造时机:当
PhoneBook类中声明Contact _contacts[8]时,这8个Contact对象属于PhoneBook的成员变量。在**PhoneBook构造函数执行之前**,C++会自动调用这8个Contact的默认构造函数,这就是日志开头8次Contact created的来源——这部分完全在你写的PhoneBook构造函数代码执行之前发生。 - 构造函数中的额外操作:你在
PhoneBook构造函数里写了_contacts[i] = Contact();,这里的Contact()会创建一个临时Contact对象(触发1次Contact created),然后通过赋值操作把临时对象的内容拷贝到_contacts[i]中,赋值完成后临时对象被销毁(触发1次Contact destroyed)。循环8次就产生了后面的8组构造+析构日志。 - 析构时机:当
PhoneBook对象销毁时,数组里的8个Contact会被依次析构,但这部分日志未出现在你提供的实际输出中(可能是输出截断),但核心问题是构造顺序和额外的临时对象操作导致日志不符合预期。
动态分配版本符合预期的原因
当把_contacts声明为Contact *_contacts,并在PhoneBook构造函数中执行_contacts = new Contact[8];时:
- 指针本身只是一个变量,不会自动触发
Contact的构造函数。 new Contact[8]会在**PhoneBook构造函数执行过程中**,主动调用8次Contact的默认构造函数,所以日志顺序是PhoneBook created之后,紧接着8次Contact created,符合你的预期。- 当
PhoneBook析构时,delete[] _contacts会主动调用8次Contact的析构函数,然后才输出PhoneBook destroyed,完全匹配预期的日志顺序。
额外建议
如果想使用数组版本且符合预期日志,只需要删除PhoneBook构造函数中的循环赋值代码即可——因为数组的8个Contact已经在PhoneBook构造前被默认构造完成,不需要再手动赋值。修改后的PhoneBook构造函数如下:
PhoneBook::PhoneBook(): _size(0) { std::cout << "PhoneBook created" << std::endl << std::flush; // 移除循环赋值代码 }
这样日志顺序会变为:8次Contact created(数组构造)→ PhoneBook created → 程序结束时8次Contact destroyed → PhoneBook destroyed。如果想要PhoneBook created先输出,只能使用动态分配或者将数组改为std::array<Contact,8>并在构造函数中显式构造(本质和动态分配逻辑一致)。
内容的提问来源于stack exchange,提问作者BobDeTunis
相关产品推荐
相关产品推荐

