应该崩溃但不会
这句话应该持保留态度。 C++ 没有“必须崩溃”的概念。它有一个未定义行为的概念,这可能会也可能不会导致崩溃。即便如此,您的代码也没有未定义的行为。
两个实例的 c_str 指的是同一个地方(这个我明白,复制
构造函数逐位复制,字符串内部存储数据
char*,指针被复制。
您正在谈论std::string 的实现。您必须改为查看其接口,以确定哪些操作是安全的,哪些不安全。
除此之外,您正在谈论的实现称为写时复制或“COW”,is obsolete since C++11。最新的 GCC 版本已放弃它。
见GCC 5 Changes, New Features, and Fixes:
std::string 的新实现默认启用,使用
小字符串优化而不是写时复制引用计数。
小字符串优化也使用了相同的技术,例如,在 std::string 的 Visual C++ 实现中。它以完全不同的方式工作,因此如果您使用足够新的 GCC 版本,那么您对 std::string 内部工作原理的理解将不再正确,或者如果您使用 Visual C++,则它永远不会正确。
但问题是,为什么这段代码不会崩溃?
因为它根据其接口的文档正确使用std::string 操作,并且因为您的编译器没有完全损坏。
您基本上是在问为什么您的编译器会为正确的代码生成有效的二进制文件。
a1 和 a2 是堆栈变量,
是的(正确的说法是对象具有“自动存储期限”)。
当解构它们时,字符串 B 也会被解构,这些字符串的内部 char*(指向相同的内存位置)不会被删除两次吗?
您的编译器的std::string 实现确保不会发生这种情况。要么它根本不使用 COW,要么析构函数包含检查共享缓冲区是否已被删除的代码。
如果您使用的是较旧的 GCC 版本,那么您只需查看您的 std::string 实现的源代码即可了解它是如何完成的。毕竟它是开源的——但要小心,因为它可能看起来有点吓人。例如,这是an older GCC version 的析构函数代码:
~basic_string()
{ _M_rep()->_M_dispose(this->get_allocator()); }
然后查看_M_dispose(在同一个文件中),您会发现这是一个非常复杂的实现,包含各种检查和同步。
还要考虑这个:
如果复制std::string 的行为会导致崩溃,那么整个课程将毫无意义,不是吗?