Это сообщение будет немного большим, поэтому извините заранее. Во всяком случае, я получаю исключение при запуске моей программы в режиме отладки (Visual Studio 2010), что я не могу вполне понять, почему происходит:Доступ к информации о местонахождении доступа 0xfeeefe2 по вызову деструктора
Unhandled exception at 0x5524ad4a (msvcp100d.dll) in CppTest1.exe: 0xC0000005: Access violation reading location 0xfeeefef2.
Я совершенно новой для C++, и я пытаюсь реализовать Pimpl-подобную структуру класса модели, как в следующем:
declaration.h
#include <vector>
class A
{
public:
struct aStruct
{
std::string aString;
std::vector<std::string> moreStrings;
};
A();
~A();
void addSomething(aStruct thing);
private:
class Implementation;
Implementation* instance;
};
class B
{
public:
B();
~B();
void doWork();
private:
class Implementation;
Implementation* instance;
};
class A::Implementation
{
public:
Implementation();
~Implementation();
void addSomething(aStruct thing);
private:
std::vector<A::aStruct> bunchOfStructs;
};
class B::Implementation
{
public:
Implementation();
~Implementation();
void doWork();
private:
A member;
};
main.cpp
#include "declaration.h"
A::A() : instance(new Implementation) {}
A::~A() { delete instance; }
void A::addSomething(aStruct thing) { instance->addSomething(thing);}
A::Implementation::Implementation() {}
A::Implementation::~Implementation() {}
void A::Implementation::addSomething(aStruct thing) { bunchOfStructs.push_back(thing);}
B::B() : instance(new Implementation) {}
B::~B() {delete instance;}
B::Implementation::Implementation() { doWork();}
B::Implementation::~Implementation() {}
void B::Implementation::doWork()
{
A a;
member = a;
}
int main(int argc, char* argv[])
{
B b;
return 0;
}
Теперь, когда код выходит из основного блока, вызывается деструктор для B, который, в свою очередь, удаляет реализацию, которая, в свою очередь, вызывает деструктор для экземпляра реализации, который она хранит, и так далее, пока деструктор для векторного bunchOfStructs не будет сохранен в экземпляре А называется. И здесь он терпит неудачу.
стек вызов выглядит следующим образом:
msvcp100d.dll!std::_Container_base12::_Orphan_all() Line 201 + 0x12 bytes C++
CppTest1.exe!std::vector<A::aStruct,std::allocator<A::aStruct> >::_Tidy() Line 1304 + 0xb bytes C++
CppTest1.exe!std::vector<A::aStruct,std::allocator<A::aStruct> >::~vector<A::aStruct,std::allocator<A::aStruct> >() Line 706 C++
CppTest1.exe!A::Implementation::~Implementation() Line 8 + 0x2b bytes C++
CppTest1.exe!A::Implementation::`scalar deleting destructor'() + 0x2b bytes C++
CppTest1.exe!A::~A() Line 4 + 0x50 bytes C++
CppTest1.exe!B::Implementation::~Implementation() Line 14 + 0x2b bytes C++
CppTest1.exe!B::Implementation::`scalar deleting destructor'() + 0x2b bytes C++
CppTest1.exe!B::~B() Line 12 + 0x50 bytes C++
CppTest1.exe!main(int argc, char * * argv) Line 24 + 0x12 bytes C++
Насколько я понимаю 0xFEEEFEEE это шаблон заливки используется в Visual Studio во время отладки. И сообщение об исключении указывает, что я пытаюсь получить доступ к уже удаленному? Но я не совсем понимаю, почему это происходит. Когда-то, я думаю, что-то уничтожается. Если бы не назначение member = a
, код будет запущен. Кроме того, если бы я реализовал это без шаблона, он, казалось бы, работал нормально. Например:
thisworks.h
#include <vector>
class C
{
public:
struct aStruct
{
std::string aString;
std::vector<std::string> moreStrings;
};
C();
~C();
void addSomething(aStruct thing);
private:
std::vector<C::aStruct> bunchOfStructs;
};
class D
{
public:
D();
~D();
void doWork();
private:
C member;
};
main.cpp
#include "thisworks.h"
C::C() {}
C::~C() {}
void C::addSomething(aStruct thing) { bunchOfStructs.push_back(thing); }
D::D() { doWork(); }
D::~D() {}
void D::doWork()
{
C c;
member = c;
}
int main(int argc, char* argv[])
{
D d;
return 0;
}
Теперь я понимаю, что есть, вероятно, более эффективные способы сделать это, однако, так как я все еще пытаюсь научиться C++ и Я уже потратил некоторое время на то, чтобы понять, почему это проблема, в первую очередь мне очень хотелось бы понять проблему.
Цель использования pimpl заключается в том, чтобы скрыть внутренние детали класса от клиентов, которые включают заголовок. Но похоже, что вы также вставляете детали реализации в заголовок - если вы собираетесь это сделать, зачем вообще беспокоиться о pimpl? – greatwolf
Немедленно обратите внимание, что ошибка находится в 'msvcp100d.dll', что означает, что std-объект, вероятно, будет очищен дважды.Образец байта, который вы видите, заполняется для RT, когда он уже освободил базовую память. Таким образом, что-то, что * держит * объект std, вероятно, будет дважды удалено или доступно после удаления. – WhozCraig
Вы нарушаете правило трех, что, вероятно, и является причиной, как указывает Enigma. http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three –