I know this is very basic but somehow working on different technologies I have mashed up my C++ concepts
I have created a simple program but it is giving exception when the destructor is called.
Below is the code:
#include "stdafx.h"
#include<iostream>
using namespace std;
class Employee
{
public:
Employee(char *name){
cout<<"Employee::ctor\n";
myName_p = new char(sizeof(strlen(name)));
myName_p = name;
}
void disp(){cout<<myName_p;}
~Employee()
{
cout<<"Employee:dtor\n\n";
delete myName_p;
}
private:
char *myName_p;
};
int main()
{
Employee emp("check");
emp.disp();
return(0);
}
Requesting all to clear this basic concept. As per my understanding we can't use delete[] because we are not using new[] in this case. Though I have tried using delete[] , but still it was giving error
You REALLY should use std::string here.
It is so much easier, especially for a beginner. The list of errors is:
you are calculating the wrong size of the name, it should be strlen(name)+1, not using sizeof anything.
You also should use new char[strlen(name)+1].
You are copying the data from the string supplied as the argument to the constructor, use strcpy rather than name_p = name - the latter leaks the memory you just allocated, and then you have a pointer to a const char * which you should not delete.
If you fix the allocation so that it is correct, you should then use delete [] name_p;.
However, it you instead use std::string, all of the above problems go away completely, you can just do:
Employee(char *name) name_p(name) { ... }
and get rid of all the problematic new, delete and copying. Of course, name_p is probably no longer a suitable name for the variable, but you get the idea.
Change
myName_p = new char(sizeof(strlen(name)));
myName_p = name;
to
myName_p = strdup(name);
and #include <cstring>. This creates new space and copies the parameter string. In this way, you'll have to call free instead of delete in your destructor.
Otherwise, after the second assignment, you have assigned the string literal "check" to myName_p, and the newly created space is discarded. Then your destructor tries to delete "check" rather than the allocated space, which results in crash.
Also, it is better practice to use std::string rather than old char* strings:
class Employee
{
public:
Employee(char *name): myName_p(name) {
cout<<"Employee::ctor\n";
}
void disp(){ cout << myName_p; }
private:
std::string myName_p;
};
The string class will manage memory for you.
Related
I have a problem,
I have to write a paper about Dynamic Memory Management in C++, however I never learned C++.
For my example code I want to show, why you need to use a Deconstructor if you allocate memory using new in your Class. But I can't get my code to work.
How do I write my Constructor, so that the string name gets allocated with the new operator?
Code i already have:
class Studi
{
private:
unsigned int matNr;
char* name;
public:
Studi(const string& k_name, unsigned int k_matNr)
{
name = new char[k_name.length()];
strcpy(name, k_name);
matNr = k_matNr;
}
};
thanks for any help :)
You have to allocate one more byte for the terminating null-character.
You can use c_str() to obtain C-style string from std::string.
Studi(const string& k_name, unsigned int k_matNr)
{
name = new char[k_name.length() + 1];
strcpy(name, k_name.c_str());
matNr = k_matNr;
}
Also don't forget to follow The Rule of Three to avoid memory management troubles when the object is copied.
I am not going to past the whole source because it is 1000+ rows, but I have specially constructed a similar case about the matter I am interested in. Pay attention to this source code:
#include <iostream>
using namespace std;
class Person
{
public:
Person();
Person(char*);
~Person();
Person& operator=(const Person&);
friend Person& example(const Person&);
void print() const;
private:
char* name;
};
Person::Person()
{
name = new char[12];
name = "Temp";
}
Person::~Person()
{
delete[] name;
}
Person::Person(char* _name)
{
name = new char[strlen(_name)+1];
strcpy_s(name,strlen(_name)+1,_name);
}
Person& example()
{
char* TestName = new char[11];
TestName = "ShouldStay";
Person B(TestName);
return B;
}
void Person::print() const
{
cout << name;
}
int main()
{
example();
return 0;
}
In this case the example() function will return:
example returned {name=0x007cad88 "îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ... } Person &
So obviously the destructor is called on the return command and delete the memory in the heap (so I am not able to do anything further with the pointer - it is pointing to already freed memory - no data).
My question is - How to avoid such behavior? What is the most elegant way to avoid such issue?
Thank you in advance!
Use string rather than char[] to avoid having to use new.
Return Person rather than Person&, as locally-scoped classes
are destroyed after they leave scope. Although this will cause a
copy to happen depending on compiler settings. This depends of course upon providing a proper copy constructor.
To guarantee avoiding a copy, change the signature of example to:
void example(Person& person)
And fill in the fields of the inputted person inside the function. The scope of that Person will be bound to the calling scope (or wherever else you constructed it). This method has drawbacks though such as you cannot chain the results together.
Your code contains many logical errors:
Person::Person()
{
name = new char[12];
name = "Temp";
}
In the above function you allocate a char array of 12 elements, then you simply forget about it and instead make name pointing to a string literal.
Person::~Person()
{
delete[] name;
}
whoopps. In case Person was build from a default constructor this would delete a string literal. A no-no in C++.
Person::Person(char* _name)
{
name = new char[strlen(_name)+1];
strcpy_s(name,strlen(_name)+1,_name);
}
Not 100% sure what strcpy_s is, but the code in this case allocates an array and seems to copy the string into the array. This seems ok (but just strcpy(name, _name); would have been better for many reasons).
Person& example()
{
char* TestName = new char[11];
TestName = "ShouldStay";
Person B(TestName);
return B;
}
This code is seriously broken. First of all it's returning by reference a temporary object. A Very Very Bad Idea. It's also allocating an array, and once again just forgetting about it and using a string literal instead.
The most elegant way (actually the ONLY way in my opinion) to get your code working is to first understand how the basics of C++ work. You should start first by reading a good C++ book from cover to cover, and only then you should start coding in C++.
Your 1000 lines of source code are most probably just rubbish. I'm not saying you're dumb, just that you don't know the basics of C++. Take care of them first by reading, not experimenting with a compiler.
You cannot learn C++ by experimenting for two reasons:
It's a complicate and sometimes even just downright illogical language because of its history. Guessing is almost always a bad move. No matter how smart you are there's no way you can guess correctly what a committee decided.
When you make a mistake there are no runtime error angels to tell you so. Quite often it happens that apparently the program works anyway... until it's run by your teacher, boss or spouse. Guessing C++ rules by writing code and observing what happens is nonsense.
Person& example()
{
char* TestName = new char[11];
TestName = "ShouldStay";
Person B(TestName);
return B;
}
The above creates an instance of Person on the stack, scoped to the function. So when the function returns, the destructor for Person is called and a reference to the destroyed object (on the stack) is returned.
In addition you should either consider returning a copy of Person, or you need to use the new operator to create an instance of person on the heap and return a pointer to that.
So i have the following class
class Community
{
private:
char* Name;
char foundationDate[11];
Person* founder;
int maxMembersCount;
int membersCount;
Person* members;
static int communitiesCount;
.....
and i want to implement a copy constructor :
Community::Community(const Community& other)
{
this->Name = new char[strlen(other.Name)+1];
strcpy(this->Name,other.Name);
strcpy(this->foundationDate,other.foundationDate);
this->founder = other.founder;
this->maxMembersCount = other.maxMembersCount;
this->membersCount = other.membersCount;
this->members = new Person[this->maxMembersCount];
this->members = other.members;
communitiesCount++;
}
but this code crashes whenever i say Community A=B;
so for me this code seems legit, but when i start debugging there is the message: this-> "unable to read memory". Please help me if you need more code example please let me know.
Community::Community(const char* name , char foundDate[],Person* founder,int maxMembers) {
this->Name = new char[strlen(name)+1];
strcpy(this->Name,name);
strcpy(this->foundationDate,foundDate);
this->founder = new Person(founder->getName(),founder->getEGN(),founder->getAddress());
this->maxMembersCount = maxMembers;
this->membersCount = 2;
this->members = new Person[this->maxMembersCount];
communitiesCount++;
}
this is the main constructor of the class which works just fine....
There are multiple problems here, any of whichi could be part or all of the problem.
If Name or foundationDate is not null-terminated on the right-hand side, it will run off and copy bad memory.
If founder or members are owned by the object, you will either leak memory if you don't delete them in the destructor, or cause a whole variety of memory-related problems when you shallow-copy and then delete twice, etc.
To fix this, just make your Name and foundationDate std::string, and then make founder and members be owned by value rather than by pointer. If you absolutely have to allocate them on the heap use a smart pointer such as shared_ptr to hold it instead of a bug-prone raw pointer.
First of all, check that other.Name is filled with a pointer to a null-terminated string, that other.foundationDate contains a null-terminated string. That is, you pass good pointers to strlen and strcpy.
If that's true, check that B in the assignment is accessible altogether.
If that's true too, printf everything. And debug where exactly the exception occurs. Or post whole code that is compilable and which reproduces the error.
Also note that here:
this->members = new Person[this->maxMembersCount];
this->members = other.members;
the first assignment does nothing (leaks memory, in fact) while the second double deletes your memory upon object destruction (if you properly delete[] members).
I have a class that holds a few vectors, I'm not sure which method is the best but when the I call the destructor they should be deleted from memory.
HEADER:
class Test
{
public:
Test();
~Test();
void AddString(char* text);
void AddString(string text);
private:
char * StringToCharPointer(string value);
vector<char*> *pVector;
}
CPP File:
Test::Test()
{
};
Test::~Test()
{
vector<char*>::iterator i;
for ( i = pVector->begin() ; i < pVector->end(); i++ )
{
delete * i;
}
delete pVector;
};
char * Test::StringToCharPointer(string value)
{
char *pChar = new char[value.length()];
strcpy(pChar, value.c_str());
return pChar;
};
Test::AddString(char* text)
{
pVector->push_back(text);
};
Test::AddString(string text)
{
pVector->push_back(StringToCharPointer(text));
};
so here's pretty much all the methods that I use, but what's wrong?
Firstly, i is an iterator on the vector, it is not the pointer stored in the vector. *i is the pointer stored in the vector, so if you're going to delete anything it should be that.
Secondly, delete *i is only valid if the object pointed to by *i was allocated with new. Not new[], not malloc, and it doesn't point to a string literal. Since you don't say how your data was allocated, it is not possible for us to say whether or not you are freeing it correctly.
It seems likely that you should use a std::vector<std::string>.
Update for updated question:
HEADER:
class Test
{
public:
Test();
~Test();
void AddString(const string &text);
private:
vector<string> mVector;
};
CPP file:
Test::Test()
{
};
Test::~Test()
{
};
void Test::AddString(const string &text)
{
mVector.push_back(text);
};
Your destruction code looks fine (although I guess you meant delete *i; in the second snippet, since otherwise, ti wouldn't have even compiled.
However, the errors you are getting indicate you put bad things in your vectors. The only char*s that can be inserted in the vector with such destruction code are the ones returned by new char. Especially, you must not insert literals ("abc") or strings that are made as parts of other strings (strtok(NULL, ":"), strchr(str, ':') into it.
This is one obvious problem: char *pChar = new char[value.length()];. You are doing new[] but doing delete in destructor which invoked undefined behavior. You should use delete[] to delete those pointers. But using delete[] might give problems for Test::AddString(char* text) method as you can not be sure how memory for text is allocated i.e. using new or new[] or malloc. The simplest way is to use std::vector<std::string> as suggested by Steve Jossep.
It seems likely that you should use a std::vector<std::string>, to shorten the wise words of Steve Jessop.
To elaborate a little more: you say you want "to make the memory allocation smaller", but sounds like you're at the wrong path if you don't know pointers, and correct me if I'm wrong in guessing premature optimization (usually the case with inexperienced developers in this type of question).
Have a homework assignment in which I'm supposed to create a vector of pointers to objects
Later on down the load, I'll be using inheritance/polymorphism to extend the class to include fees for two-day delivery, next day air, etc. However, that is not my concern right now. The final goal of the current program is to just print out every object's content in the vector (name & address) and find it's shipping cost (weight*cost).
My Trouble is not with the logic, I'm just confused on few points related to objects/pointers/vectors in general. But first my code. I basically cut out everything that does not mater right now, int main, will have user input, but right now I hard-coded two examples.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Package {
public:
Package(); //default constructor
Package(string d_name, string d_add, string d_zip, string d_city, string d_state, double c, double w);
double calculateCost(double, double);
~Package();
private:
string dest_name;
string dest_address;
string dest_zip;
string dest_city;
string dest_state;
double weight;
double cost;
};
Package::Package()
{
cout<<"Constucting Package Object with default values: "<<endl;
string dest_name="";
string dest_address="";
string dest_zip="";
string dest_city="";
string dest_state="";
double weight=0;
double cost=0;
}
Package::Package(string d_name, string d_add, string d_zip, string d_city, string d_state, string r_name, string r_add, string r_zip, string r_city, string r_state, double w, double c){
cout<<"Constucting Package Object with user defined values: "<<endl;
string dest_name=d_name;
string dest_address=d_add;
string dest_zip=d_zip;
string dest_city=d_city;
string dest_state=d_state;
double weight=w;
double cost=c;
}
Package::~Package()
{
cout<<"Deconstructing Package Object!"<<endl;
delete Package;
}
double Package::calculateCost(double x, double y){
return x+y;
}
int main(){
double cost=0;
vector<Package*> shipment;
cout<<"Enter Shipping Cost: "<<endl;
cin>>cost;
shipment.push_back(new Package("tom r","123 thunder road", "90210", "Red Bank", "NJ", cost, 10.5));
shipment.push_back(new Package ("Harry Potter","10 Madison Avenue", "55555", "New York", "NY", cost, 32.3));
return 0;
}
So my questions are:
I'm told I have to use a vector
of Object Pointers, not Objects.
Why? My assignment calls for it
specifically, but I'm also told it
won't work otherwise.
Where should I be creating this
vector?
Should it be part of my Package
Class? How do I go about adding
objects into it then?
Do I need a copy constructor? Why?
What's the proper way to deconstruct
my vector of object pointers?
Any help would be appreciated. I've searched for a lot of related articles on here and I realize that my program will have memory leaks. Using one of the specialized ptrs from boost:: will not be available for me to use. Right now, I'm more concerned with getting the foundation of my program built. That way I can actually get down to the functionality I need to create.
Thanks.
A vector of pointers can be reused for storing objects of sub-classes:
class Person
{
public:
virtual const std::string& to_string () = 0;
virtual ~Person () { }
};
class Student : public Person
{
const std::string& to_string ()
{
// return name + grade
}
};
class Employee : public Person
{
const std::string& to_string ()
{
// return name + salary
}
};
std::vector<Person*> persons;
person.push_back (new Student (name, grade));
person.push_back (new Employee (name, salary));
person[0]->to_string (); // name + grade
person[1]->to_string (); // name + salary
Ideally the vector should be wrapped up in a class. This makes memory management easier. It also facilitates changing the support data structure (here an std::vector) without breaking existing client code:
class PersonList
{
public:
Person* AddStudent (const std::string& name, int grade)
{
Person* p = new Student (name, grade);
persons.push_back (p);
return p;
}
Person* AddEmployee (const std::string& name, double salary)
{
Person* p = new Employee (name, salary);
persons.push_back (p);
return p;
}
~PersonList ()
{
size_t sz = persons.size ();
for (size_t i = 0; i < sz; ++i)
delete persons[i];
}
private
std::vector<Person*> persons;
};
So we can re-write our code as:
{
PersonList persons;
Person* student = persons.AddStudent (name, grade);
Person* employee = persons.AddEmployee (name, salary);
student.to_string ();
employee.to_string ();
} // The memory allocated for the Person objects will be deleted when
// `persons` go out of scope here.
Getting familiar with the Rule of Three will help you decide when to add a copy constructor to a class. Also read about const correctness.
Question 1:
You mentioned inheritance. Since inherited objects often need more bytes of storage, they don't fit into the place of a base object. If you try to put them in, you get a base object instead. This is called object slicing.
Question 2:
Design first, before you write code. There are a bunch of possible solutions.
For a start you can keep it in main(), but later you will be forced to make a class like PackageContainer for holding your objects.
Question 3 + 4:
You need a copy constructor, an assignment operator= and a destructor, when a class object owns dynamically allocated objects (the Rule of the Big Three). So a PackageContainer will probably need them.
You create objects dynamically using new Object(..). You are responsible for destroying them and for giving their memory back to the system immediately before your vector of pointers is destroyed:
for (size_t i = 0; i < shipment.size(); ++i)
{
delete shipment[i];
}
Since working with naked pointers to dynamically allocated objects is not safe, consider using
std::vector<tr1::shared_ptr<Package> > shipment;
instead or
std::vector<std::shared_ptr<Package> > shipment;
if your compiler understands C++0x. The shared_ptr handles freeing memory for you: It implements the Rule of the Big Three for one object pointer. It should be used in production quality code.
But try to get it right with naked pointers also. I think that's what your homework assignment is about.
I'm told I have to use a vector of Object Pointers, not Objects. Why? My assignment calls for it specifically, but I'm also told it won't work otherwise.
Usually, one would avoid using vector of objects to avoid the problem of Object Slicing. To make polymorphism work You have to use some kind of pointers. I am not sure of how the classes in your assignment are aligned but probably you might have Inheritance there somewhere and hence if vector is storing objects of Base class and you insert objects of Derived class in it then it would cause the derived class members to slice off.
The Best solution will be to use a smart pointer instead of a Raw pointer. The STL has an auto_ptr, but that cannot be used in a standard container.Boost smart pointers would be a best solution but as you already said you can't use Boost So in your case you can use your compiler's implementation of smart pointers, which comes in TR1 namespace,remember though that there is some disagreement on the namespace for TR1 functions (Visual C++ puts them in std::, while GCC puts them in std::tr1::).
Where should I be creating this vector? Should it be part of my Package Class? How do I go about adding objects into it then?
Your example code already has an example of adding a pointer to Package class in a vector. In a nutshell you will dynamically allocate pointers to Package and then add them to the vector.
Do I need a copy constructor? Why?
The copy constructor generated by the compiler does member-wise copying. Sometimes that is not sufficient. For example:
class MyClass {
public:
MyClass( const char* str );
~MyClass();
private:
char* str;
};
MyClass::MyClass( const char* str2 )
{
str = new char[srtlen( str2 ) + 1 ];
strcpy( str, str2 );
}
Class::~Class()
{
delete[] str;
}
In this case member-wise copying of str member will not duplicate the buffer (only the pointer will be copied(shallow copy)), so the first to be destroyed copy sharing the buffer will call delete[] successfully and the second will run into Undefined Behavior. You need deep copying copy constructor (and assignment operator as well) in such a scenario.
When to use a custom copy constructor is best defined by the Rule Of Three:
Whenever you are writing either one of Destructor, Copy Constructor or Copy Assignment Operator, you probably need to write the other two.
What's the proper way to deconstruct my vector of object pointers?
You will have to explicitly call delete on each contained pointer to delete the content it is pointing to.
vector::erase
Removes from the vector container and calls its destructor but If the contained object is a pointer it doesnt take ownership of destroying it.
Check out this answer here to know how to corrctly delete a vector of pointer to objects.