为什么这个程序没有崩溃? (浅拷贝)

Why isn't this program crashing? (shallow copy)

我正在学习复制构造函数和浅拷贝和深拷贝。我正在关注这个视频:Copying and Copy Constructors in C++

以下代码是直接从视频中复制过来的,并演示了浅拷贝...(在视频9:30处)

此代码应该在 运行 之后崩溃,因为解构器将尝试释放相同的内存两次(第一次用于 string,然后用于 string2)。第一次删除应该可以正常工作,但第二次删除应该会导致程序崩溃,因为我们正在尝试删除未分配的内存。

令人惊讶的是,这并没有发生在我的案例中。我在我的命令提示符下使用 g++ copying_and_copy_constructor.cpp 编译了代码,它编译得很好,运行 使用 a.exe。没有错误。

#include<iostream>
#include<cstring>
#include<string>

using std::endl;
using std::cout;


class String
{
private:
    char* m_Buffer;
    unsigned int m_size;
public:
    String(const char* string)
    {
        m_size = strlen(string);
        m_Buffer = new char[m_size+1];
        memcpy(m_Buffer,string,m_size);
        m_Buffer[m_size] = 0;
    }

    ~String()
    {
        delete [] m_Buffer;
    }


    friend std::ostream& operator << (std::ostream& stream, const String& string);
};


std::ostream& operator<<(std::ostream& stream, const String& string)
{
    stream<< string.m_Buffer;
    return stream;
}


int main()
{
    String string = "My string";
    String string2 = string;

    cout<<string2<<endl;
    cout<<string;
    return 0;
}

我什至尝试检查调试器。我使用 g++ -g copying_and_copy_constructor.cppgdb a.exe 编译代码。这是它的输出:

GNU gdb (GDB) 7.6.1
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from D:\a.exe...done.
(gdb) start
Temporary breakpoint 1 at 0x401446: file copying_and_copy_constructor.cpp, line 43.
Starting program: D:\a.exe
[New Thread 44892.0x8258]
[New Thread 44892.0x6df8]
[New Thread 44892.0xadd8]
[New Thread 44892.0x9658]

Temporary breakpoint 1, main () at copying_and_copy_constructor.cpp:43
43              String string = "My string";
(gdb) c
Continuing.
My string
My string[Inferior 1 (process 44892) exited normally]
(gdb)
The program is not being run.
(gdb) q

这一行让我很困惑My string[Inferior 1 (process 44892) exited normally]。这段代码如何正常退出?

视频中的人正在使用 VS-Code...我怎么会在命令提示符中出现相同的错误?

崩溃如视频所​​示:

(我现在还没有vs code(以后可能会安装))

对我来说 (Fedora 32 x86_64) 它确实崩溃了:

$ g++ -o q q.C -Wall -g;./q
My string
free(): double free detected in tcache 2
Aborted

但我绝对同意这样的代码可能看起来 运行 不错。这就是为什么有 ASAN:

$ g++ -o q q.C -Wall -g -fsanitize=address;./q
My string
=================================================================
==3388436==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0:
    #0 0x7fb76f262cd7 in operator delete[](void*) (/lib64/libasan.so.6+0xb2cd7)
    #1 0x4016e9 in String::~String() /home/jkratoch/t/q.C:25
    #2 0x40144b in main /home/jkratoch/t/q.C:42
    #3 0x7fb76ecbc041 in __libc_start_main ../csu/libc-start.c:308
    #4 0x40120d in _start (/quad/home/jkratoch/t/q+0x40120d)

0x602000000010 is located 0 bytes inside of 10-byte region [0x602000000010,0x60200000001a)
freed by thread T0 here:
    #0 0x7fb76f262cd7 in operator delete[](void*) (/lib64/libasan.so.6+0xb2cd7)
    #1 0x4016e9 in String::~String() /home/jkratoch/t/q.C:25
    #2 0x40143e in main /home/jkratoch/t/q.C:43
    #3 0x7fb76ecbc041 in __libc_start_main ../csu/libc-start.c:308

previously allocated by thread T0 here:
    #0 0x7fb76f2621d7 in operator new[](unsigned long) (/lib64/libasan.so.6+0xb21d7)
    #1 0x4015d6 in String::String(char const*) /home/jkratoch/t/q.C:18
    #2 0x4013a0 in main /home/jkratoch/t/q.C:42
    #3 0x7fb76ecbc041 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: double-free (/lib64/libasan.so.6+0xb2cd7) in operator delete[](void*)
==3388436==ABORTING

如果您不想甚至不能使用 -fsanitize=address 重新编译程序,您可以使用 Valgrind(但它速度较慢并且无法检测到所有内容):

$ g++ -o q q.C -Wall -g;valgrind ./q
==3388447== Memcheck, a memory error detector
==3388447== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3388447== Using Valgrind-3.16.0 and LibVEX; rerun with -h for copyright info
==3388447== Command: ./q
==3388447== 
My string
==3388447== Invalid free() / delete / delete[] / realloc()
==3388447==    at 0x483C59C: operator delete[](void*) (vg_replace_malloc.c:649)
==3388447==    by 0x40139E: String::~String() (q.C:25)
==3388447==    by 0x401277: main (q.C:42)
==3388447==  Address 0x4db5c80 is 0 bytes inside a block of size 10 free'd
==3388447==    at 0x483C59C: operator delete[](void*) (vg_replace_malloc.c:649)
==3388447==    by 0x40139E: String::~String() (q.C:25)
==3388447==    by 0x40126B: main (q.C:43)
==3388447==  Block was alloc'd at
==3388447==    at 0x483B582: operator new[](unsigned long) (vg_replace_malloc.c:431)
==3388447==    by 0x401334: String::String(char const*) (q.C:18)
==3388447==    by 0x40121B: main (q.C:42)
==3388447== 
My string==3388447== 
==3388447== HEAP SUMMARY:
==3388447==     in use at exit: 0 bytes in 0 blocks
==3388447==   total heap usage: 3 allocs, 4 frees, 73,738 bytes allocated
==3388447== 
==3388447== All heap blocks were freed -- no leaks are possible
==3388447== 
==3388447== For lists of detected and suppressed errors, rerun with: -s
==3388447== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)