Почему следующий код показывает ошибку времени выполнения в Ideone, а также в визуальной студии, но не в блоках кода?
#include <iostream>
using namespace std;
class myclass{
int *p;
public:
myclass(int i)
{p=new int;
*p=i;}
~myclass(){delete p;}
int *get(){return p;}
};
void show (myclass x){
int *i=x.get();
cout<<*i<<endl;
}
int main() {
myclass a(19);
show (a);
return 0;
}
И я также не знаю, что не так с этим кодом (ошибка времени выполнения как в идеоне, так и в visual studio, но не в блоках кода)
#include <iostream>
using namespace std;
int main() {
int *i=new int ;
int j=19;
i=&j;
cout<<*i<<endl;
delete i;
return 0;
}
У меня есть догадка в первом случае память p удаляется один раз в функции show(), а затем снова после main(), поэтому удаление такой же памяти может вызвать эту ошибку времени выполнения (я не уверен, хотя, будь то случай или нет, PLS объяснить почему) и использование ссылки в void show (myclass & x) действительно устранили ошибку, но я не вижу, что изменилось, а во втором случае я думаю, что ошибка использует адрес j, если назначено значение j, ошибка исчезнет, но в обоих случаях блоки кода не показали мне какой-либо ошибки, поэтому, если кто-либо сможет разъяснить это поведение компилятора, это будет очень заметно (жаль для публикации такого длинного вопроса). Thnks заранее.
Ваш класс управляет динамически распределенными ресурсами, но не соответствует правилу из трех. Ваш вызов для show
включает в себя копию объекта myclass
. Это приводит к тому, что два объекта "управляют" одной и той же памятью. Один из них уничтожается при выходе из show()
, его ресурсы де-распределяются посредством вызова для delete
в деструкторе. Это оставляет объект в main()
удерживая указатель на выделенную память, которая затем пытается вызвать delete
.
Это неопределенное поведение. Это означает, что программа может проявлять некоторую очевидную ошибку, но она также может запускаться и выходить молча.
Суть в том, что если вашему классу необходимо управлять ресурсами (и это важно, если), то следуйте правилу из трех (или пяти в С++ 11).
Итак, это типичный случай "неопределенного поведения". И да, в вашем примере myclass
, p
удаляется дважды, что и вызывает проблемы. Во втором случае вы просто удаляете указатель, который не был назначен new
, что означает, что вы не следуете правилам, как вам следует.
Неопределенное поведение - это то, где C и C++ (и другие языки) объясняют, что происходит, говоря "он не определен". Спецификация делает это, чтобы избежать необходимости исключать или делать дорогостоящие обходные пути на оборудовании, которое/не имеет определенного поведения - например, если стандарт C++ сказал, что "среда выполнения C++ должна обнаруживать delete
памяти, которая hasn 't было выделено new
", было бы очень сложно написать delete
которое делает правильную вещь (вы можете подумать, что это не слишком сложно, а для данной архитектуры это может быть не так, но в 16-разрядном микроконтроллере, это может добавить значительные накладные расходы. Поскольку одним из основных принципов C или C++ является "вы не платите за то, что не просили", такие накладные расходы были бы неприемлемы. [Неважно, схема не может быть сделана на 100% безопасной, так как всегда возможно, что указатель может быть "поддельным" и по-прежнему соответствовать всем проверкам, которые были сделаны каким-то образом].
Вам нужно перегрузить конструктор копирования.
void show (myclass x){
int *i=x.get(); // here x.p points to same location as a.p
cout<<*i<<endl;
}
//here x.p is deleted in the destructor for x and so is a.p.
Как уже упоминалось, рекомендуется следовать правилу три, если задействовано динамическое распределение памяти.