_crtisvalidheappointer(block) 删除基础 class 指针时
_crtisvalidheappointer(block) when deleting base class pointer
抱歉这么久 post,但我需要帮助才能理解这一点。
我有下面的代码抛出 _crtisvalidheappointer(block)
#include <iostream>
#include <string>
using namespace std;
#define ASSERT_ERROR
/*#define SOLVE_ASSERT*/
class Player
{
std::string playerName;
public:
Player(std::string &playerName) :playerName(playerName) { cout << "Player Constructed\n"; }
void printPlayerName() const
{
std::cout << playerName << endl;
}
#if defined SOLVE_ASSERT
virtual ~Player() { cout << "Player Destructed\n"; }
#else
~Player() { cout << "Player Destructed\n"; }
#endif
};
#if defined ASSERT_ERROR
class Batsman : virtual public Player
#else
class Batsman : public Player
#endif
{
public:
Batsman(std::string playerName) : Player(playerName) { cout << "Batsman info added\n"; }
~Batsman() { cout << "Batsman Destructed\n"; }
};
int main()
{
Player *ptr = new Batsman("Sachin Tendulkar");
ptr->printPlayerName();
delete ptr;
}
一旦我取消注释下面的行,我就解决了这个问题。
#define SOLVE_ASSERT
我在下面提到 link 那里说单继承不会导致问题。
但在我的例子中,我只是使用 virtual 来继承 class,这有点要求我使析构函数成为虚拟的。为什么会这样?
为什么下面这行代码在没有虚拟继承的情况下是合法的,为什么在虚拟继承的情况下不合法?
Player *ptr = new Batsman(playerName);
在我开始之前,这是我用来调试此代码的稍微修改过的版本:
#include <iostream>
#include <string>
using namespace std;
class Player {
std::string playerName;
public:
Player(std::string &playerName) :playerName(playerName) {
cout << "Player Constructed @" << this << endl;
}
void printPlayerName() const {
std::cout << playerName << endl;
}
~Player() {
cout << "Player Destructed @" << this << endl;
}
};
class Batsman : virtual public Player {
public:
Batsman(std::string playerName):
Player(playerName)
{
cout << "Batsman info added @" << this << endl;
}
~Batsman() {
cout << "Batsman Destructed @" << this << endl;
}
};
int main() {
Player *ptr = new Batsman("Sachin Tendulkar");
ptr->printPlayerName();
delete ptr;
}
如果我们尝试 运行 您当前状态下的代码,我们会得到以下输出消息(尽管指针值可能会有所不同):
Player Constructed @0x1048c68
Batsman info added @0x1048c60
Sachin Tendulkar
Player Destructed @0x1048c68
*** Error in `./a.out': free(): invalid pointer: 0x0000000001048c68 ***
Aborted (core dumped)
如果我们查看指针值,我们会注意到内存在错误的位置被释放。对于另一个例子,请考虑以下代码:
int* arr = new int[100];
cout << "Array is @" << arr << endl << "Memory being freed is @" << (arr+50) << endl;
delete[] (arr+50);
如果我们 运行 这个,我们得到的输出与我们在你的例子中得到的输出非常相似:
Array is @0xd8cc20
Memory being freed is @0xd8cce8
*** Error in `./a.out': free(): invalid pointer: 0x0000000000d8cce8 ***
Aborted (core dumped)
我们收到此错误是因为 0xd8cc20
和 0xd8cce8
之间的 200 个字节实际上并未被释放,从而造成内存泄漏。回到您的程序,我们注意到 0x1048c60
和 0x1048c68
之间的字节没有被释放。为了解决这个问题,我们必须确保调用正确的析构函数,我们可以通过以下两种方式之一来实现:
- 我们可以将
ptr
声明为 Batsman*
而不是 Player*
。这会
告诉编译器调用 Batsman
析构函数而不是 Player
构造函数,成功释放所有内存。
- 我们可以使
~Player()
函数 virtual
。这表明
编译器将确定要调用的正确函数
运行时间。如果这样做, Batsman
析构函数将被调用,即使
尽管该对象被声明为指向 Player
. 的指针
编辑:以下是为什么从 class Batsman : virtual public Player
中删除 virtual
似乎可以修复错误的解释:
当您使用 virtual
继承时,子类将在 超类之前 分配内存。因此,超类开始分配其内存的时间点将在对象实际启动之后很久。这解释了为什么在您的示例中,Batsman
对象在 Player
之前分配了 8 个字节。当 free()
尝试使用超类地址解除分配时,它会发现那里没有分配对象,从而导致错误。
同时,在正常的继承中,子类和超类都会从同一个地址开始分配。这意味着您可以使用超类地址释放内存。由于在超类的地址分配了一个指针,C++ 内存管理系统就好像它是一个有效的释放一样。
抱歉这么久 post,但我需要帮助才能理解这一点。
我有下面的代码抛出 _crtisvalidheappointer(block)
#include <iostream>
#include <string>
using namespace std;
#define ASSERT_ERROR
/*#define SOLVE_ASSERT*/
class Player
{
std::string playerName;
public:
Player(std::string &playerName) :playerName(playerName) { cout << "Player Constructed\n"; }
void printPlayerName() const
{
std::cout << playerName << endl;
}
#if defined SOLVE_ASSERT
virtual ~Player() { cout << "Player Destructed\n"; }
#else
~Player() { cout << "Player Destructed\n"; }
#endif
};
#if defined ASSERT_ERROR
class Batsman : virtual public Player
#else
class Batsman : public Player
#endif
{
public:
Batsman(std::string playerName) : Player(playerName) { cout << "Batsman info added\n"; }
~Batsman() { cout << "Batsman Destructed\n"; }
};
int main()
{
Player *ptr = new Batsman("Sachin Tendulkar");
ptr->printPlayerName();
delete ptr;
}
一旦我取消注释下面的行,我就解决了这个问题。
#define SOLVE_ASSERT
我在下面提到 link 那里说单继承不会导致问题。
但在我的例子中,我只是使用 virtual 来继承 class,这有点要求我使析构函数成为虚拟的。为什么会这样?
为什么下面这行代码在没有虚拟继承的情况下是合法的,为什么在虚拟继承的情况下不合法?
Player *ptr = new Batsman(playerName);
在我开始之前,这是我用来调试此代码的稍微修改过的版本:
#include <iostream>
#include <string>
using namespace std;
class Player {
std::string playerName;
public:
Player(std::string &playerName) :playerName(playerName) {
cout << "Player Constructed @" << this << endl;
}
void printPlayerName() const {
std::cout << playerName << endl;
}
~Player() {
cout << "Player Destructed @" << this << endl;
}
};
class Batsman : virtual public Player {
public:
Batsman(std::string playerName):
Player(playerName)
{
cout << "Batsman info added @" << this << endl;
}
~Batsman() {
cout << "Batsman Destructed @" << this << endl;
}
};
int main() {
Player *ptr = new Batsman("Sachin Tendulkar");
ptr->printPlayerName();
delete ptr;
}
如果我们尝试 运行 您当前状态下的代码,我们会得到以下输出消息(尽管指针值可能会有所不同):
Player Constructed @0x1048c68
Batsman info added @0x1048c60
Sachin Tendulkar
Player Destructed @0x1048c68
*** Error in `./a.out': free(): invalid pointer: 0x0000000001048c68 ***
Aborted (core dumped)
如果我们查看指针值,我们会注意到内存在错误的位置被释放。对于另一个例子,请考虑以下代码:
int* arr = new int[100];
cout << "Array is @" << arr << endl << "Memory being freed is @" << (arr+50) << endl;
delete[] (arr+50);
如果我们 运行 这个,我们得到的输出与我们在你的例子中得到的输出非常相似:
Array is @0xd8cc20
Memory being freed is @0xd8cce8
*** Error in `./a.out': free(): invalid pointer: 0x0000000000d8cce8 ***
Aborted (core dumped)
我们收到此错误是因为 0xd8cc20
和 0xd8cce8
之间的 200 个字节实际上并未被释放,从而造成内存泄漏。回到您的程序,我们注意到 0x1048c60
和 0x1048c68
之间的字节没有被释放。为了解决这个问题,我们必须确保调用正确的析构函数,我们可以通过以下两种方式之一来实现:
- 我们可以将
ptr
声明为Batsman*
而不是Player*
。这会 告诉编译器调用Batsman
析构函数而不是Player
构造函数,成功释放所有内存。 - 我们可以使
~Player()
函数virtual
。这表明 编译器将确定要调用的正确函数 运行时间。如果这样做,Batsman
析构函数将被调用,即使 尽管该对象被声明为指向Player
. 的指针
编辑:以下是为什么从 class Batsman : virtual public Player
中删除 virtual
似乎可以修复错误的解释:
当您使用 virtual
继承时,子类将在 超类之前 分配内存。因此,超类开始分配其内存的时间点将在对象实际启动之后很久。这解释了为什么在您的示例中,Batsman
对象在 Player
之前分配了 8 个字节。当 free()
尝试使用超类地址解除分配时,它会发现那里没有分配对象,从而导致错误。
同时,在正常的继承中,子类和超类都会从同一个地址开始分配。这意味着您可以使用超类地址释放内存。由于在超类的地址分配了一个指针,C++ 内存管理系统就好像它是一个有效的释放一样。