C++ 仅在派生类时才给出SEG错误的代码!

C++ 仅在派生类时才给出SEG错误的代码!,c++,C++,我无法在我编写的以下代码中找到错误[尽管不是出于任何目的]。 我已经对可疑的代码行进行了评论。 我正在使用sun Studio 12。 这是一种未定义的行为。不管你是否推导出它,它在任何情况下都会引起问题。 将const char*分配给char*时,如下所示: ptr -> ch = const_cast < char* >("ar0"); 一旦您尝试将某些内容分配给ch,它将给出编译错误。因此,它将使您编写包装器来分配ch,在那里您可以处理解除分配 memcp

我无法在我编写的以下代码中找到错误[尽管不是出于任何目的]。

我已经对可疑的代码行进行了评论。 我正在使用sun Studio 12。


这是一种未定义的行为。不管你是否推导出它,它在任何情况下都会引起问题。 将
const char*
分配给
char*
时,如下所示:

ptr -> ch = const_cast < char* >("ar0");
一旦您尝试将某些内容分配给
ch
,它将给出编译错误。因此,它将使您编写包装器来分配
ch
,在那里您可以处理解除分配

memcpy(ptr -> ch,"ar\0",4);
memcpy
将内容从源缓冲区复制到指针指向的目标缓冲区

ptr -> ch = const_cast < char* >("ar0");
ptr->ch=const_cast(“ar0”);

您正在将值重新分配给指针本身。这是危险的,因为您再也无法在堆上获取原始缓冲区。这是内存泄漏。另外,在析构函数中删除这个指针,它现在不指向堆上的缓冲区。它是未定义的。

我认为您在理解未定义行为的含义方面存在严重问题

未定义的行为不一定意味着错误 对不起,我大声喊叫,但这是非常重要的一点

未定义的行为,正如名字所暗示的,“未定义”。这意味着你不知道会发生什么。事实上,一个断层是你能期待的最好的东西。。。但不幸的是,C++中的90%的UB只是静默。 该应用程序将正常运行,甚至会提供您期望的结果。一切都会好起来的。。。当然,在演示日之前,应用程序会在100人面前严重崩溃,只是为了在youtube上看到你惊呆的脸

未定义的行为是通过实验学习C或C++的原因之一。当你在C++中出错时,语言不会帮助你…沉默的假设基本上是你不会犯任何错误。。。如果你正在学习这门语言,这当然是一个很难达到的要求

未定义的行为也是在C或C++中编写测试套件时太多希望的原因。在这些语言中(以及UB存在的其他语言中),编写代码时不经过太多思考(因此让bug进来),然后希望以后删除它们,这是一种非常愚蠢的方法。虽然这种先节省思考时间,然后用调试时间来交换的想法通常是不好的方法(去除bug的成本/努力总是会更高),但在允许UB的语言中,这是一种真正的自杀,因为bug也可以隐藏在非确定性行为后面

显然,我并不是说测试是个坏主意。。。当然,这是一个很好的方法(如果你想保持重构的可能性,基本上是必须的),但是只有当你在编写代码时没有把它作为降低注意力的借口时才可以

基本上,您应该避免混淆错误(即UB)和崩溃(即segfault)。我们是朋友。。。您希望有尽可能多的崩溃,因为崩溃是错误存在的信号。例如,要添加更多可能的崩溃点,您应该使用断言。。。segfault只是环境自动放置的断言,但您需要更多。当你有一个崩溃,然后你知道有一个错误,你可以开始寻找它。不幸的是,当你没有崩溃,并不意味着没有错误。。。这仅仅意味着没有任何虫子落入你为它们准备的陷阱

一段代码可以很好地编译(零错误和零警告),它可以通过整个测试套件。。。但同时也可能是不正确的。当然,即使在更高级别的语言中也是如此,在这些语言中,为避免UB而付出的性能代价值得运行时检查(例如Java),但逻辑级别更高。C和C++中的UB使事物变得更难,因为不确定性。 在您的代码中,UB被触发,因为您正在调用
delete[]
指针,而该指针不是通过调用
new获得的。。。[]
(字符串文字)。这当然可能会也可能不会产生segfault

您的代码还有一些其他合法但非常可疑的部分:

  • 基类析构函数不是虚拟的。您没有使用指向基的指针销毁派生对象,因此这是合法的,但无论如何,这是一个坏主意。如果要派生类,则析构函数应该是虚拟的

  • 派生类定义了析构函数,但没有复制构造函数和赋值运算符:要么不定义它们,要么定义(或至少声明)所有三个。原因是,如果这三种方法不存在,编译器会自动创建它们,并且自动生成的代码在一种情况下不太可能是正确的,而在其他情况下则不可能是正确的。例如,在您的代码中,如果有人使用
    derived*der2=new-derived(*der1)创建派生对象的副本指向动态分配内存的指针将被复制并可能被释放两次(两个被销毁的对象各释放一次);如果赋值为
    *der2=*der1相反,除了内存泄漏之外,还可能出现双重释放。禁止复制类的构造函数和赋值可能是有道理的,但在这种情况下,常见的习惯用法是将这些方法声明为私有,而不实现它们(如果其他人试图使用它们,这将导致编译错误,如果方法错误地使用它们,则会导致链接错误)


  • 对你是对的。但当我不是从基类派生时,为什么我没有得到seg错误呢?还是因为UB我没有任何seg故障?是的,我
    memcpy(ptr -> ch,"ar\0",4);
    
    ptr -> ch = const_cast < char* >("ar0");