继承自CString导致内存不足异常

时间:2014-02-12 19:44:10

标签: c++ mfc out-of-memory cstring

我有以下字符串类,它继承自MFC CString

class TString : public CString
{
public:
    TString() : CString(_T(""))
    {
    }

    TString(LPCTSTR str) : CString(str)
    {
    }
};

我在一个经常使用带有TString的+运算符的方法中有一个内存不足的异常,所以我尝试进行如下的测试

TString str;
TCHAR buffer[] = "Hello world, Hello world, Hello world, Hello world, Hello world, Hello world";

uint i = 0;
while(i++ < 100000000)
{
    str = buffer;
    str += buffer;
}

占用了大量内存并以内存不足结束,这是在执行最后一个代码后从任务管理器更改内存的一个镜头

enter image description here

当我用CString替换TString时,正常花了时间并且没有内存不足异常且内存在任务管理器中是稳定的,如此镜头

enter image description here

我尝试了以下两种状态

  1. 我创建了另一个exe应用程序,它依赖于包含TString类的同一个库,它工作得非常好,如CString
  2. 我在while循环中调用了Sleep(1),它给了内存的快速变化,也很稳定 我该怎么做才能解决这个问题?
  3. 编辑:

    当我重新编译解决方案时,我的解释有一些错误CString也有相同的行为,即使是std :: string我认为新的运算符重载我创建了一个自定义类,调用malloc并在析构函数中释放它也导致为了相同的行为,我将测试代码移动到我的应用程序的第一个入口点,我的应用程序的构造函数继承自CWinAppEx,代码工作正常然后我查看了InitInstance我发现内存泄漏检测4行代码如下

    int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
    tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;//<====this is the evil after comment everything is fine
    tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
    _CrtSetDbgFlag(tmpDbgFlag);
    

    我总是知道这是一个人为的错误。这会造成灾难。

    我刚评论了这一行

      

    tmpDbgFlag | = _CRTDBG_DELAY_FREE_MEM_DF;

    解决了它。 这个答案还谈到了这个标志的用法Finding heap corruption 我感谢所有人都试图帮助解决这个问题,我希望它会有所帮助。

2 个答案:

答案 0 :(得分:2)

无论如何,继承一个没有虚析构函数的类是一个非常糟糕的主意。以下是Scott Meyers的“Effective C ++:55改进程序和设计的具体方法,第三版”的引用。

  

问题是getTimeKeeper返回一个指向派生类的指针   对象(例如,AtomicClock),该对象正通过基础删除   类指针(即TimeKeeper *指针)和基类   (TimeKeeper)有一个非虚拟析构函数。这是一个食谱   灾难,因为C ++指定派生类对象时   通过指向非虚拟基类的指针删除   析构函数,结果未定义。通常在运行时发生的事情   是对象的派生部分永远不会被销毁。如果打电话   getTimeKeeper碰巧返回一个指向AtomicClock的指针   object,对象的AtomicClock部分(即数据成员   在AtomicClock类中声明)可能不会被销毁,   也不会运行AtomicClock析构函数。但是,基类部分   (即,TimeKeeper部分)通常会被破坏,从而领先   一个好奇的“部分被摧毁”的对象。这是一个很好的方式   泄漏资源,破坏数据结构,并花费大量时间   调试器。

总而言之,当您从没有虚拟析构函数的类派生时,您可以使用指向该对象的指针,删除该指针时该对象仅被部分销毁。这显然只发生在指针指向基类时,而不是指针指向类本身时。

如果必须扩展没有虚析构函数的类,最好通过包含来实现。这是创建一个新类,它不是从您希望扩展的类派生的。然后有一个你希望扩展的类型的成员变量,并实现你希望扩展的类中的所有函数作为你希望扩展的类中相应函数的包装器。

例如。

class TString /* do not derive from CString */
{
private:
  CString m_string;
    TString() :
      m_string()
  {
  }

  TString(const TCHAR *str) :
    m_string(str)
  {
  }

  int GetLength() const
  {
    return m_string.GetLength();
  }

  /* All the other functions in the CString class here. */

  /* Your additions to the CString class here. */
}

当然,在最近的MFC版本中,CString实际上是一个模板类,所以你应该让你的类成为模板类以及如下。

template< typename BaseType >
class TStringT
{
    /* The same as above but use BaseType instead of TCHAR. */
}

然后执行以下操作。

typedef TStringT< wchar_t > TStringW;
typedef TStringT< char > TStringA;
typedef TStringT< TCHAR > TString;

我希望这会有所帮助。

答案 1 :(得分:0)

这可能不是您问题的答案,但请尝试一下。

将以下方法添加到您的TString类:

TString& operator=(LPCTSTR src)
{
    CString::operator=(src);

    return( *this );
}

这将加速从LPTSTR(例如str = buffer;)分配TString。如果没有该方法,则从LPTSTR分配TString将在执行赋值之前构造临时TString。如果您使用调试器单步调试,您将看到。