3

I have the following string class that inherits from MFC CString

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

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

I had an out of memory exception in a method that used the + operator with TString frequently, so I tried to make a test like the following

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;
}

that took a lot of memory and ended with out of memory exception this is a shot for the memory change from the task manager after the last code was executed

enter image description here

when I replaced the TString with the CString it was normal took the time and no out of memory exception and the memory was stable in task manager as in this shot

enter image description here

I tried the following 2 states

  1. I created another exe application that depend on the same Library that contain the TString class it worked very fine like CString
  2. I caled Sleep(1) inside the while loop and it gave to rapid change in the memory and was stable also What can I do to fix such a problem??

EDIT:

There were some wrong in my explanation when I recompiled the solution CString also had the same behaviour even the std::string I thought the new operator overloading I created a custom class that call the malloc and free at the destructor it also lead to the same behaviour, I moved that test code to the first entry point in my application to constructor of my application that inherits from CWinAppEx the code worked fine then I looked at the InitInstance I found a memory leak detection 4 lines of code as the following

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);

I always know that it's a human mistake. That cause disasters.

I just commented the line

tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF;

That solved it. this answer also talk about the usage of this flag Finding heap corruption I thank everyone tried to help in this problem, I hope that it will help.

Community
  • 1
  • 1
ahmedsafan86
  • 1,776
  • 1
  • 26
  • 49
  • 1
    1) Why are you inheriting from CString? 2) Verify that the CString-only code didn't cause the compiler to optimize away that loop. – PaulMcKenzie Feb 12 '14 at 20:01
  • @PaulMcKenzie: really I'm Porting old code written on the top of OWL library to use MFC the string type that was used was TString and has some custom methods and it is heavily used in the code, so I make it inherit from CString and provide those methods, now the software is under test and that was a bug. – ahmedsafan86 Feb 12 '14 at 20:12
  • when you inherit a class you need to redefine all constructors and operators, I think – cha Feb 12 '14 at 23:28
  • BTW, how about creating a typedef? – cha Feb 12 '14 at 23:46
  • @cha: I said that the big problem is there are custom methods for Tstring used heavily in the other code and it is not in the CString. and the operators are already inherited, but I defined the constructors that call the parent CString Constructors. – ahmedsafan86 Feb 13 '14 at 07:37
  • I used your test code in a debug Environment and can not see any Change in memory usage. I used the process Explorer to check this. – xMRi Feb 13 '14 at 08:43
  • @xMRi: have read all of my question?? I told you it worked in some some projects, some cases not working in others. please read the whole question well. – ahmedsafan86 Feb 13 '14 at 09:01
  • The code snippet you Show (and it is not complete because the Default constructor is missing) has no influence on the memory usage. As Long as we see not the complete code THIS is not the reason. – xMRi Feb 13 '14 at 09:11
  • @xMRi:I edit it it is no complete, sorry I written it and missed that part, the other methods in TString are not used in this test never used so no need to write them all, because each one call a global public methods and no need for that all. – ahmedsafan86 Feb 13 '14 at 09:35
  • Did you implement `operator +` and/or `operator +=` in your `TString` class? If so, show us the code. – Henrik Feb 13 '14 at 14:24
  • @Henrik: no just constructors and helper methods for loading string from specific dlls resources no operators at all, I has one project crash report that uses WTL CString and the main application depends on that crash report can this caused some ambiguity to linker? I don't know the TString was inheriting from the CString coming with WTL library and was working well, now after i remove the dependency on OWL and WTL except that crash report the dependency on MFC CString caused that strange behavior. – ahmedsafan86 Feb 13 '14 at 21:56
  • @Henrik: I wand to mention that when I put a break point inside the loop and press F5 many times it stay stable, you see the Sleep(1) fixed it then the free don't happen immediately it need some time to happen! – ahmedsafan86 Feb 13 '14 at 22:10
  • The TString version works fine here, no memory spikes whatsoever, but it is about twice as slow than CString version. This is because the assignment `str = buffer` uses a temporary TString variable before doing the assignment. If you execute the statement step by step you will see what happens exactly. – Jabberwocky Feb 14 '14 at 09:45
  • I edited the question added the cause of the problem and the solution – ahmedsafan86 Feb 15 '14 at 10:49

2 Answers2

2

Inheriting from a class that does not have a virtual destructor is a very bad idea anyway. The following is a quote from "Effective C++: 55 Specific Ways to Improve Your Programs and Designs, Third Edition" by Scott Meyers.

The problem is that getTimeKeeper returns a pointer to a derived class object (e.g., AtomicClock), that object is being deleted via a base class pointer (i.e., a TimeKeeper* pointer), and the base class (TimeKeeper) has a non-virtual destructor. This is a recipe for disaster, because C++ specifies that when a derived class object is deleted through a pointer to a base class with a non-virtual destructor, results are undefined. What typically happens at runtime is that the derived part of the object is never destroyed. If a call to getTimeKeeper happened to return a pointer to an AtomicClock object, the AtomicClock part of the object (i.e., the data members declared in the AtomicClock class) would probably not be destroyed, nor would the AtomicClock destructor run. However, the base class part (i.e., the TimeKeeper part) typically would be destroyed, thus leading to a curious “partially destroyed” object. This is an excellent way to leak resources, corrupt data structures, and spend a lot of time with a debugger.

To summarize, when you derive from a class that does not have a virtual destructor, you then make use of a pointer to that object, the object is only partially destroyed when you delete that pointer. This apparently only happens when the pointer is to the base class and not when the pointer is to the class itself.

If you must extend a class that does not have a virtual destructor, it is best to do it by containment. That is create a new class that is not derived from the class you wish to extend. Then have a member variable of the type that you wish to extend and implement all of the functions in the class you wish to extend as wrappers around the appropriate function in the class you wish to extend.

For example.

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. */
}

Of course in recent versions of MFC CString is actually a template class so you should perhaps make your class a template class as well as follows.

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

Then do the following.

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

I hope this helps.

Benilda Key
  • 2,836
  • 1
  • 22
  • 34
0

This may not be the answer to your problem but give it a try.

Add following method to your TString class:

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

    return( *this );
}

This will speed up the assignment of a TString from a LPTSTR such as str = buffer;. Without that method assignment of a TString from a LPTSTR will construct a temporary TString before doing the assignment. If you step through the assignment with a debugger you will see.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115