The C++ compiler I'm using is an older version (C++98 maybe?)
I can NOT dynamically allocate memory from the system pool using things like new, malloc
I can however use built-in OS calls to malloc from a heap array defined by me.
I'm running into some strange behavior (program crashes) when I do the following
class cBaseClass /* pure abstract */
{
public:
virtual void ifFunc( void ) = 0;
virtual ~cBaseClass() = 0;
}
inline cBaseClass::~cBaseClass()
{
}
class cDclass:cBaseClass
{
public:
cDclass();
~cDclass();
void ifFunc( void ); /* implement the pure virtual */
}
cDclass::cDclass( void )
{
printf("[0x%X] derived constructor called\n", this);
}
cDclass::~cDclass( void )
{
printf("[0x%X] derived destructor called\n", this);
}
void cDclass::ifFunc(void)
{
printf("[0x%X] ifFunc called from derived class\n", this);
}
uchar_t myHeap[4096];
int main ( void )
{
cDclass* pMyPtr = NULL;
uint32_t i = 0;
( void ) memset( myHeap, 0, sizeof(myHeap)/sizeof(myHeap[0]);
for( i = 0; i < 20; i++)
{
pMyPtr = myHeap[i * sizeof(cDclass) + 4];
*pMyPtr = cDclass();
pMyPtr->ifFunc(); /* Crash */
}
}
What I see is that the constructor for the derived class gets called..then its destructor gets called and then a crash.
Am I mistaken in believing that *pMyPtr = cDclass() constructs a class and then makes a copy of that class at the address specified by pMyPtr?
I say this because when I remove
pMyPtr = cDClass()
and create a dummy variable to store an instance of cDclass and then use memmove it no longer crashes.
The first weird thing is that this line:
pMyPtr = myHeap[i * sizeof(cDclass) + 4];
compiles without complaint. It's implicitly converting a uchar_t to a cDclass*, which shouldn't be possible without a reinterpret_cast. But it could be that your compiler is more lenient there. Anyway, at least you're missing a & operator here.
The second problem is that yes, you are wrong in your assumption. What the line does is construct a temporary object on the stack, then assume that at the pointer location there is already a fully constructed object, call that object's compiler-generated copy assignment operator with the temporary as an argument, then destroy the temporary.
The thing that never happens is that an object is actually constructed in the memory. This means the vptr is never initialized, so the call to the virtual function is a null dereference.
What you need to do is use placement-new to construct an object in-place. Your loop should look like this:
int main ( void )
{
uint32_t i = 0;
( void ) memset( myHeap, 0, sizeof(myHeap)/sizeof(myHeap[0]);
for( i = 0; i < 20; i++)
{
// get the address to construct the object at
uchar_t* pMyAddr = &myHeap[i * sizeof(cDclass) + 4];
// construct a new object in-place at that address
cDclass* pMyPtr = new (pMyAddr) cDclass();
pMyPtr->ifFunc(); /* Don't crash */
}
}
Note that you will be responsible for actually calling the destructor for all the objects you created.
*pMyPtr = cDclass();
Assumes that a valid object already exists at *pMyPtr and uses its assignment operator. (On a typical implementation, this is probably not good enough because it doesn't copy the vptr.)
What you need instead is
new(pMyPtr) cDclass;
to call the constructor at a specified memory location. You'll need to #include <new>.
Related
class mapInfo
{
public:
mapInfo();
~mapInfo();
public:
int dataType_m;
private:
int *frequency;
};
//constructor is defined here.
mapInfo::mapInfo() :
dataType_m(0),
frequency(NULL)
{
}
//destructor is defined here
mapInfo::~mapInfo()
{
free(frequency);
frequency = NULL;
}
Result_t Maps::add(mapInfo &mapInfo_r)
{
if (maps_mp == NULL)
{
numMaps_m = 1;
maps_mp = (mapInfo *) calloc(1, sizeof(mapInfo));
}
else
{
numMaps_m++;
maps_mp = (mapInfo *) realloc(maps_mp, numMaps_m*sizeof(mapInfo));
}
maps_mp[numMaps_m-1] = mapInfo_r; // Default copy constructor
return 1;
}
While compiling with gcc8, getting the following compilation error. It looks like defining the destructor like above giving the compilation error for gcc8.
How to resolve this?
error: 'void* realloc(void*, size_t)' moving an object of non-trivially copyable type 'class xyyz::mapInfo'; use 'new' and 'delete' instead [-Werror=class-memaccess].
That’s simply not proper C++. Rewrite your code as follows (I’m guessing here with regards to the type of frequency, but definitely don’t use free on it):
#include <vector>
class map_info
{
public:
map_info();
private:
int data_type;
std::vector<int> frequency;
};
std::vector<map_info> maps_mp;
map_info::map_info() : data_type(0), frequency() {}
// …
void maps::add(map_info& map_info)
{
maps_mp.push_back(map_info);
}
maps_mp = (mapInfo *) realloc(maps_mp, numMaps_m*sizeof(mapInfo));
This is not sensible. You can't just move an object from one aree of memory to another if that object is non-trivial.
For example, consider a string object that keeps a pointer to the string. It could look like this:
class MyString
{
char* inner_ptr;
char buf[64];
...
};
And it might have a constructor like this:
MyString::MyString (const char* j)
{
if (strlen(j) < 64)
inner_ptr = buf;
else
inner_ptr = malloc (strlen(j) + 1);
strcpy(inner_ptr, j);
}
And a destructor like this:
MyString::~MyString()
{
if (buf != inner_ptr)
free (inner_ptr);
}
Now, think about what happens if you call relloc on an array of these. The short strings will still have their inner_ptrs pointing to the old object's buffer, which you just deallocated.
The error message explains this issue reasonable well. It is simply not legal to use realloc to move a non-trivial object. You have to construct a new object because the object needs a chance to handle the change in its address.
I've got a class named Area and when I create an Area object I need to keep its address. So in Area's constructor I use the following command:
Area *p = this->Area;
and I get an error saying:
"invalid use of Area::Area".
Any idea of what's going wrong?
this is already a pointer to that object. So you should make something like this:
Area *p = this;
The this pointer is an implicit parameter to all member functions (non-static members). Therefore, inside a member function, this may be used to refer to the invoking object.
I create an Area object I need to keep its address.
Both self-reference and self-ptr may be initialized in the initializer list of the ctor. (and it is also easy to do in a method)
class Area
{
private:
Area& selfRef; // size is 8 bytes
Area* selfPtr; // size is 8 bytes - but why bother, just use this
char data[1000]; // size is 1000 bytes
public:
Area() : selfRef(*this), selfPtr(this)
{
for (int i=0; i<1000; ++i) data[i] = 0; // initialize data
};
void foo() {
// easy to do in a method:
Area& localSelfRef = *this; // 8 bytes
// ...
localSelfRef.bar(); // instead of ptr
this->bar(); // direct use of this ptr
selfRef.bar(); // use of class initialized selfRef
}
void bar() {
// ...
}
}
Class size is 1000+ bytes.
selfPtr and SelfRef and localSelfRef are each 8 bytes (on my system).
class A
{
public:
A() {}
A(int _x) : x(_x) {}
private:
int x;
};
int main()
{
A a[100](1); //compile error
A ptr = new A[100](1); // compile error
return 0;
}
As we know, the sample code will encounter compile error, because an object array can be initialized only by default constructor.
Why cannot an object be initialized using a constructor with parameter?
I recently read the book Inside the C++ Object Model. As the book mentioned, when initializing an object array, the process will call a function (named vec_new in cfront 2.0) which accepts a constructor pointer and call the constructor without any arguments. It means the function can only call the default constructor, so an object array can be initialized only using the default constructor.
But, why does the compiler use vec_new to initialize an object array?
If the compiler does not call the vec_new, it can expand the code to initialize all the objects in an array like the sample code:
int main()
{
A ptr = new A[100](1);
////// expanded code, I suppose
/** 1. allocate memory to ptr
* 2. constuct 100 object respectively
* int i = 0;
* char *tmp_ptr = (char *)ptr;
* while(i++ < 100) {
* tmp_ptr = A::A(tmp_ptr, 1);
tmp_ptr += sizeof(A);
}
*/
return 0;
}
I confuse that if a compiler implement "initializing an object array using constructor with arguments"?
I have some code that creates an array of documents. Each document object has an array of document-wide values, and an array of individual files (called lines because each is a line in the source file I'm reading from) that together have all the document data. When I attempt to add a document object to the array, it is calling my copy constructor below:
CMyDocument::CMyDocument(CMyDocument& cSourceDoc)
{
m_lpastrFields = new CStringArray;
m_lpacLines = new CArray<CMyLine, CMyLine>;
int nCount;
int nSize;
nSize = static_cast<int>(cSourceDoc.GetFields()->GetSize());
for (nCount = 0; nCount < nSize; nCount++)
{
m_lpastrFields->Add(cSourceDoc.GetFields()->GetAt(nCount));
}
nSize = static_cast<int>(cSourceDoc.GetLines()->GetSize());
for (nCount = 0; nCount < nSize; nCount++)
{
m_lpacLines->Add(cSourceDoc.GetLines()->GetAt(nCount));
}
m_strDocDate = cSourceDoc.GetDocDate();
m_nDocID = cSourceDoc.GetDocID();
m_strDocType = cSourceDoc.GetDocType();
}
The problem is, when I try to access the documents by pulling them from the document array later, the two arrays I've copied above are empty. The seem to be initialized and have memory addresses, but they contain no data. The member variables are populated though. I'm not sure if I'm doing the copying incorrectly or if the problem is elsewhere.
EDIT: The regular constructor looks like this:
CMyDocument::CMyDocument()
{
m_lpastrFields = new CStringArray;
}
I don't new the m_lpacLines object in this case because it is passed into the MyDocument object through a function called InitDocument. I may as well include that here. (Some unnecessary details, like the way I parse the strLine variable to extract all the values, were trimmed for brevity's sake.
void CMyDocument::InitDocument(CMyColumns* lpcColumns, CString strLine, CArray<CMyLine, CMyLine>* lpacLines)
{
CString strValue;
CString strComma = ",";
int nPos = 0;
m_lpacLines = lpacLines;
while (-1 != nPos)
{
strValue = strLine.Tokenize(strComma, nPos);
m_lpastrFields->Add(strValue);
}
m_strDocDate = m_lpastrFields->GetAt(lpcColumns->GetDocDateIndex());
CString strDocID = m_lpastrFields->GetAt(lpcColumns->GetDocIDIndex());
m_nDocID = atoi(strDocID);
m_strDocType = m_lpastrFields->GetAt(lpcColumns->GetDocTypeIndex());
}
And to be clear, I am newing the lpacLines object outside of the InitDocument function every time I loop through. I've already debugged this code though and everything is being assigned correctly here.
SECOND EDIT: In trying to convert these all the non-pointer member variables, I am now coming up against error C2248:'CObject::CObject' : cannot access private member declared in class 'CObject'. Upon reflection, problems like this may have been what pushed me towards using pointers in the first place.
THIRD EDIT: Here is the class declaration:
class CMyDocument
{
public:
CMyDocument();
~CMyDocument();
CMyDocument(CMyDocument& cSourceDoc);
void InitDocument(CMyColumns* lpcColumns, CString strLine, CArray<CMyLine, CMyLine>* lpacLines);
inline CString GetDocDate(void) {return(m_strDocDate);};
inline int GetDocID(void) {return(m_nDocID);};
inline CString GetDocType(void) {return(m_strDocType);};
inline CStringArray* GetFields(void) {return(m_lpastrFields);};
inline CArray<CMyLine, CMyLine>* GetLines(void) {return m_lpacLines;};
private:
CArray<CMyLine, CMyLine>* m_lpacLines;
CStringArray* m_lpastrFields;
CString m_strDocDate;
int m_nDocID;
CString m_strDocType;
};
Now that you've posted the full class definition, it is clear that you are indeed violating the Rule of Three: If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
You have a copy constructor, and a destructor, but no copy assignemnt. Add these members and you should be fine.
CMyDocument& operator=(CMyDocument cSourceDoc) {
swap(cSourceDoc);
return *this;
}
void swap(CMyDocument& cSourceDoc) {
using std::swap;
swap(m_lpacLines, cSourceDoc.m_lpacLines);
swap(m_lpastrFields, cSourceDoc.m_lpastrFields);
swap(m_strDocDate, cSourceDoc.m_strDocDate);
swap(m_nDocID, cSourceDoc.m_nDocID);
swap(m_strDocType, cSourceDoc.m_strDocType);
}
Your constructor allocates memory, and makes a member point at it. Somewhere in your code you are making a copy of a CMyDocument. Since you have no copy assignment operator, the compiler uselessly made one for you, that simply copies the pointer, so that you then have two CMyDocument objects pointing at the same CArray and CStringArray. Then, when one of them is deleted, they delete the CArray and CStringArray, and the other CMyDocument is left with useless pointers that point at invalid memory. When you are attempting to use that invalid memory, sometimes, if you get lucky you'll see what used to be there. In this case, the empty CArray and CStringArray. (They empty themselves right as they are deleted). If you weren't lucky, the program would have simply crashed.
Just when I thought I had it figured out, I get an exception handling error. Problem: The problem is that the private members lose the information outside of the constructor. Here's my class definition
Code:
class ClassType
{
private:
char *cPointer;
int length;
public:
ClassType();
// default constr. needed when allocating in main.
ClassType( const ClassType* );
char otherFunc();
};
classtype.cpp:
"#include ClassType.h"
ClassType( const ClassType* )
{
cPointer = ClassType->cPointer;
length = ClassType->length;
}
ClassType::ClassType( const char *myVar )
{
cPointer = new char[ strlen( myVar ) + 1 ] //+1 for trailing '\0'
strcpy( cPointer, myVar );
length = strlen( cPointer );
}
char ClassType::otherFunc()
{
cPointer; // Nothing is shown when debugging..
cPointer = "MyPointer"; // Results in acrash
length = 5; // Results in a crash
}
// The main function is working properly.
This isn't valid C++ code.
If you are using C++, shouldn't you
use std::string for the string?
Constructor based on another
instance should be ClassType(const
ClassType& rhs)
I can't think of why it would crash where you indicate, but there are several problems with your code (some of which are compile-time problems, so we can't be sure this code accurately reflects the problem):
there are ownership problems - when ClassType::ClassType( const ClassType* ) is called, which instance of ClassType owns the object pointed to by cPointer?
there's no dtor to release the memory allocated in `ClassType::ClassType( const char *myVar )'
since cPointer may point to something allocated by new or might not, you'll have issues trying to determine when the thing allocated by new should be deleted.
As far as the compile time errors go:
the definition of ClassType( const ClassType* ) should start with ClassType::ClassType( const ClassType* )
the contents of ClassType::ClassType( const ClassType* ) should be using a parameter instead of the ClassType class name as the pointer
char ClassType::otherFunc() needs a return statement
Is this the real code?
ClassType( const ClassType* )
{
cPointer = ClassType->cPointer;
length = ClassType->length;
}
If so, it needs to be like this:
ClassType( const ClassType* rhs )
{
cPointer = rhs->cPointer;
length = rhs->length;
}
Also, this constructor is not the default ctor:
ClassType( const ClassType* ); // default constr. needed when allocating in main.
A default ctor is specifically a ctor that either takes zero parameters or all the parameters have defaults specified. In other words, a default ctor is a ctor that can be called like this:
ClassType myObject;
I provided a pretty complete answer in your other question about this code. I believe that the main issue is that your copy constructor is massively broken. It will cause double free errors and other badness. Also since your destructor calls delete on the pointers you allocate, you can't ever assign string literals to your classes pointers.
Default constructors are those for which all the arguments have default values, so your constructor that takes a pointer is not a default constructor.
Your crash locations indicate the class has not been constructed properly, so you're probably getting address errors when you assign to them.
Could you post main, as that is probably key to see the problem?