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.
Related
I have two projects, one basic client and a dynamic library.
Here's what happens in the client:
int main()
{
Scrutinizer scru;
scru.Scrutinize();
return 0;
}
In the DLL, The Scrutinizer class is as such (__declspec(dllexport) and such omitted for Clarity)
Header
class ProcessesGenerator;
class Scrutinizer
{
public:
Scrutinizer();
~Scrutinizer();
ProcessesGenerator *ProcGenerator
void Scrutinize();
};
The forward declaration of ProcessesGenerator was 'mandatory' for me to avoid some kind of circular reference.
Constructor in .cpp file
Here is how I initialize it:
Scrutinizer::Scrutinizer()
{
ProcGenerator = &ProcessesGenerator();
}
More about this ProcessesGenerator class:
Header
class ProcessesGenerator
{
public:
ProcessesGenerator();
~ProcessesGenerator();
WinFinder winFinder;
std::vector<std::string> fooCollec;
void GenerateProcesses();
};
ProcessesGenerator.cpp
Constructor:
ProcessesGenerator::ProcessesGenerator()
{
//winFinder = WinFinder();//problem will be the same with or without this line
fooCollec = std::vector<std::string>{"one", "two", "three"};
}
A breakpoint in the constructor shows that the vector is initialized with the chosen values.
Problematic function:
void ProcessesGenerator::GenerateProcesses() {
std::string foo = "bar";
fooCollec = std::vector<std::string>{};//read access violation
fooCollec.push_back(foo);//read access violation
winFinder.SomeVector= std::vector<std::string>{};//read access violation
}
Once there, I Can see that the size of vector is reset to 0. Any attempt to re-initialize it, or to push an element results in read access violation .Same with the vecotr member of its WinFinder member. I guess the flaw is obvious, but I really don't get it,
Thanks!
Your problem is with
Scrutinizer::Scrutinizer()
{
ProcGenerator = &ProcessesGenerator();
}
What you are doing is taking the address of a temporary object. That object will be destroyed and the end of that line and you will be left with a pointer that doesn't point to a valid object.
The old way to fix it would be to use
Scrutinizer::Scrutinizer()
{
ProcGenerator = new ProcessesGenerator();
}
But now you have to implement the copy constructor, copy assignment operator, and the destructor. Since you have a modern compiler what you can do instead is make ProcGenerator a std:unique_ptr<ProcessesGenerator> and then Scrutinizer() becomes
Scrutinizer::Scrutinizer() : ProcGenerator(make_unique<ProcessesGenerator>()) {}
I would also like to add that &ProcessesGenerator(); should not even compile. Unfortunately MSVS has a non-standard extension that allows this to compile. You can turn on the /Za compiler option (enforce ANSI compatibility) and then you should get an error like
error C2102: '&' requires l-value
The line ProcGenerator = &ProcessesGenerator(); makes a temporary ProcessesGenerator, takes its address and then puts it in your ProcGenerator pointer. The temporary is then destroyed, leaving garbage.
You probably wanted to be allocating it on the heap ProcGenerator = new ProcessesGenerator; but even in that case I would strongly suggest using unique_ptr instead of a raw pointer.
I have been given this definitions, the function should return what is in info->phrase. However info->phrase can contain a string in which case I can only make it return the first char on info->phrase. Is there a way to make a string compatible with the char type? I am new to c++.
struct rep_info {
int num;
char *phrase;
};
I´ve tried few thing but get type errors, this was my latest attempt
char *phrase_info(rep_info info) {
char text[std::strlen(info->phrase) + 1];
text = info->phrase;
return text;
}
Since you said you have been given these definitions, let's fix the problem with the current setup first. Looking at your function, you are trying to copy into this local array (incorrectly I might add), and return this local variable. There are a number of things wrong with this, including the syntax and the fact that the local variable is destroyed when the function exits.
If you just need to get the value of the phrase member variable, the simplest solution would be to just access the member variable directly and return it:
char *phrase_info(rep_info info) {
return info.phrase; //since info is not a pointer, use the '.' accessor
}
If you mean to pass a pointer to the function, you would re-write it like this:
char *phrase_info(rep_info *info) {
return info->phrase;
}
But it seems like you feel the need to copy the contents of info->phrase into a new memory space? If so, then you would do something like this where you first allocate new memory and return this buffer:
char *phrase_info(rep_info *info) {
char *buf = new char[std::strlen(info->phrase) + 1];
std::strcpy(buf,info->phrase); //copies info->phrase into buf
return buf;
}
You would then need to use delete on the returned memory value to clean up the memory allocated by new, otherwise you will have a memory leak.
Overall, all the above solution would potentially solve the problem given some parameters you haven't made clear. To round this out, this should be written more like:
class rep_info {
private:
int num;
std::string phrase;
public:
rep_info(int n, std::string p) : num(n), phrase(p) {}
std::string get_phrase() { return phrase; }
// other functions
};
//later in the code
rep_info info(...);
info.get_phrase();
Ideally, you would wrap these member variables into their own object with corresponding member functions that can get and set these values. Moreover, for handling strings in C++, std::string is the preferred option for storing, copying, modifying, etc. strings over the older char * C-style string.
First i want to introduce my situation :
I have write some classes that has char* pointer as private class member.And also this project has GUI, so when click buttons,some functions may execute more than one time.Those classes are designed single class in project.But some functions of them can execute more than one time.Then I found my project has memory leak.
so i want to ask the following questions:
how to design the set function?
how to design the other functions that use the char* member variable?
how to design the class operator= function?
for example:
class A:
{
public :
setStr(char * s){//need new or just use =?};
A & operator=(const A& other){//also need new?};
manyTimesFunctions(char * other)
{
//need to use chars other to assignment str
//how to carefully use new to avoid memory leak?
//other may be another class's locality none const variable
}
private:
char * str;
}
So ,the project only init class A once,but may use setStr and manyTimesFunctions many times.
May be the answer:
I think i have found what i need to take care of:copy that class,that answers are really useful to me.
Just use std::string. It takes care of memory management for you. The member declaration then looks like
std::string str;
and the setter function looks like
void setStr( char const* s ) { str = s; }
Where you want to use the string and need a char const*, just write str.c_str().
With use of standard library types like std::string, and no manual dynamic allocation, you generally don't need to be concerned about operator=: the compiler-generated copy assignment works nicely.
By the way, it's generally a good idea to decide on some naming convention for member variables. Common ones for C++ include str_, mStr, and my_str. The underscore suffix is perhaps the most common one, but don't use a leading underscore like _str, because although technically allowed it conflicts with the conventions for implementation defined names (e.g. leading underscore is not allowed for identifiers in the global namespace).
I am not 100% sure what you are trying to do. However, since char* is a pointer you may be able to simply pass around the references.
char* operator=(char* s) { str = s; }
Just know that then if you modify value in your function it will modify the place you copied it from
If the char* needs to actually be a clone, so that it does not modify the original value. You first need to obtain the length of the char*.
This can be done with this function
unsigned Length(char* s)
{
unsigned I = 0;
while( *(s+I) != '\0')
I++;
return I;
}
The a new string can be created as follows
str = new char[LENGTH];
At that point you can copy the string over term by term
for(I = 0 ; I < LENGTH; I++)
{
str[I] = s[I];
}
Finally to avoid memory leaks this needs to be deleted in the class destructor
~A()
{
delete [] str;
}
Of course using std::string could save a lot of problems.
This answer will be used to contrast what the other answer(s) given that state to use std::string (and those answers are correct -- use std::string).
Let's assume that you could only use char *, you can't for some reason use std::string, and that you are dealing with NULL terminated strings. This is a synopsis of what your implementation would have to do (and please compare this with simply using std::string):
#include <algorithm>
#include <cstring>
class A
{
public:
// construct empty string
A () : str(new char[1]()) {}
// construct from non-empty
A(const char *s) : str(new char[strlen(s) + 1])
{ strcpy(str, s); }
// copy construct
A(const A& rhs) : str(new char[strlen(rhs.str) + 1])
{ strcpy(str, rhs.str); }
// destruct
~A() { delete [] str; }
// assign
A& operator=(const A& rhs)
{
A temp(rhs);
std::swap(str, temp.str);
return *this;
}
// setter
void setStr(char * s)
{
A temp(s);
*this = temp;
}
// getter
const char* getStr() { return str; }
private:
char * str;
};
Live Example
After adding a couple more constructors and a getter function, this follows the Rule of 3.
You see how much code we needed to add just to make the class safely copyable and assignable? That's why using std::string is much more convenient than using char * when it comes to class members. For std::string a single line needs to be changed, compared to adding the copy / assignment (and move, which I didn't show) functions.
The bottom line is that in C++ if you want strings, use strings (std::string) and try to keep away from using char * (unless you have a very compelling reason to be using char * to represent string data).
I have been reading an array of SRD objects from a binary file - but since this was my first time doing so, I have modified a header making all its members public as I wasn't sure what would be going on. I have completed my assignment, all that is left is to make these members private and write methods that modify them. However, there is a problem.
For debugging purposes I put only 1 member private, and until I write all methods for it I will keep it that way. This member is simply an unsigned int C. When writing a method for returning it (getC() returns C), it is returning a value of 0000...3435973836, meaning it is not set?
So, I have an array of pointers to SRD created based on the number of objects in the binary file.
SRD *a;
...
a = new SRD[numOfRecords];
and the array is filled from the file...
for (i=0; i<numOfRecords; i++)
{
f.seekg(i * sizeof s);
f.read((char *)&a[i], sizeof s);
cout << a[i].getCC();
}
now, a[i].getCC() works when C is public, but making it private makes 000..3435...
meaning accessing it is not the problem, but it is not set in the fread from the previous for loop. I imagine I need some type of assignment operator, that sets these values, but I have no clue...
When you make the variable private, the SRD class is no longer plain old data (POD). Consequently, you have no guarantees regarding its in-memory representation, and therefore you cannot rely on f.read to work like this any longer.
I suggest that you add a read-from-stream method in your class and inside that method, read directly to the unsigned int member variable. Then, in the loop, you just call that method (passing the stream as a reference or a pointer).
EDIT, example as requested:
class SRD {
...
public:
void readFromStream(istream& f) {
f.read(&CC, sizeof CC);
}
private:
unsigned int CC;
};
Loop:
for (i=0; i<numOfRecords; i++)
{
a[i].readFromStream(f);
cout << a[i].getCC();
}
Actually, the problem was something else: the header was defined in a certain order, the first member was an array, while C was the second, and while debugging i first started with the C - and moving it from the second position to the above public one, changed the structure of the class.
so,
Class P
{
int i;
char c;
}
differs from
Class P
{
char c;
int i;
}
because the data in the binary file has a specific order, and when rearranging the order of the class defenition, a member (c) tries to access data intended for another member (i)
//In header file: class definition:
class myString
{
public:
myString(void);
myString(const char *str);
myString(const myString &); //copy constructor
~myString(void); //destructor
void swap(myString &from);
private:
char *stringPtr;
int stringLen;
};
//in cpp file, defining them member functions
myString::myString(const char *str)
{
stringLen = strlen(str);
stringPtr = new char[stringLen+1];
strcpy(stringPtr,str);
cout << "constructor with parameter called"<<endl;
}
myString::myString(const myString &str)
{
stringPtr = new char[str.stringLen +1];
strcpy(stringPtr,str.stringPtr);
cout << "copyconstructor"<<endl;
}
void myString::swap(myString &from)
{
myString buffer(from);
int lengthBuffer = from.stringLen;
from = new char[stringLen+1];
from.stringLen = stringLen;
strcpy(from.stringPtr, stringPtr);
stringPtr = new char[lengthBuffer+1];
stringLen = lengthBuffer;
strcpy(stringPtr,buffer.stringPtr);
}
You can't modify a reference. Even if you replace it with a pointer modifying a pointer will not modify an object pointed to. Instead you need to work through the reference - just swap the fields.
void myString::swap(myString &from)
{
std::swap( stringLen, from.stringLen );
std::swap( stringPtr, from.stringPtr );
}
the above is using std::swap() as suggested by user sbi in comments. This is completely equivalent to the following (just for illustration, don't reinvent STL):
void myString::swap(myString &from)
// First remember own length and pointer
const int myOldLen = stringLen;
char* myOldPtr = stringPtr;
// now copy the length and pointer from that other string
stringLen = from.stringLen;
stringPtr = from.stringPtr;
// copy remembered length and pointer to that other string
from.StringLen = myOldLen;
from.StringPtr = myOldPtr;
// done swapping
}
Both will work even when called fro self-swapping:
myString string;
string.swap( string );
You have already gotten a few good answers concerning the errors in you myString::swap() function. Yet, I'd like to add another one. There's some many things wrong with that function, I first found it hard to think of where to begin. But then I realized that you fail on some fundamental issue which I'd like to point out:
As a convention, a function called swap is expected to perform its task
in O(1)
without ever throwing an exception.
(Yes, I know, there are exceptions: std::tr1::array<>::swap(). But those should be very well justified.) Your implementation fails on both accounts. It is O(n) (strcpy) and might throw an exception (new) -- and it does so unnecessarily and without justification.
When you look at myString, you'll see that it only has two pieces of member data, which both are of built-in type. That means swapping two objects of this class is really simple to do while keeping to the conventions mentioned above: just swap the member data. That's as simple as calling std::swap on them:
void myString::swap(myString &from)
{
std::swap(this->stringPtr,from.stringPtr);
std::swap(this->stringLen,from.stringLen);
}
This is will never fail (swapping two pointers and two integers cannot fail), executes in O(1), is very easy to understand (well, once you get a grip on that swapping, anyway; it is an idiomatic form of implementing a class-specific swap function), and consists of two lines of code calling something well-tested in the standard library instead of 8 lines of code doing error-prone (and, in your case, erroneous) manual memory management.
Note 1: Once you've done this, you should specialize std::swap to call your implementation for your class:
namespace std { // only allowed for specializing function templates in the std lib
template<>
inline void std::swap<myString>(myString& lhs, myString& rhs)
{
lhs.swap(rhs);
}
Note 2: The best (simple, exception-safe, and self-assignment-safe) way to implement assignment for your class is to use its swap:
myString& myString::operator=(const myString& rhs)
{
myString tmp(rhs); // invoke copy ctor
this->swap(tmp); // steal data from temp and leave it with our own old data
return *this;
} // tmp will automatically be destroyed and takes our old data with it
from = new char[stringLen+1]; should be from.stringPtr = new char[stringLen+1]; . Also remember to free the previously allocated memory before allocating new one.
Look closely at the line
from = new char[stringLen+1];
It is the same as
from = MyString(new char[stringLen+1]);
so your constructor of MyString get uninitialized array of chars. Then you trying to get the length of the string, but strlen just looping through chars of the string looking for 0 char. As we don't know what content uninitialized array of chars might have, we don't know what length strlen could return. It can even go further than array boundary and crash your program with segfault. But I can say for sure, after that there's not enough space in from.stringPtr to hold the string you want to copy in it.
So, use from.stringPtr = new char[stringLen+1]; or better from = MyString(*this); since you have copy constructor already.