I was trying to understand Copy constructor and Operator. I read some code but I just didn't get it.
Here is main function
int main()
{
Cwin win1('A',"window")
Cwin win2;
win1 = win2;
win1.set_data('B',"hello");
win1.show();
win2.show();
}
I extract the most important code from Class Cwin
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
......
};
Output :
B : hello
B : hello
I can understand what causes it. But I can't understand below solution that fix it.
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
void operator=(const Cwin &win)
{
id = win.id;
strcpy (this->title , win.title);
}
......
};
Output:
B : hello
D : default
Why change it strcpy (title , win.title); into strcpy (this->title , win.title); makes a huge difference?
Explanation:
Inside of a member function, you can write this->title or title. Both are equivalent, unless you have a local function variable called title which name would then hide the member.
WHat makes the difference is that you have added an assignement operator.
Without assignement operator your the statement win1=win2 proceeds by copying member by member. In this situation, a pointer is copied as is, so that after the copy, both objects will point to the same pointed char*:
When you then set_data(), the string is copied to the char* pointer by win1, but win2 points to the same value.
Your second code snippet, handles the copy much better: the content of the string pointed to is copied/duplicated to char* pointed by the other object. win1 and win2 will hence continue to use 2 distinct strings.
Remarks/advices
You use strcpy() without checking that the target is long enough to contain the copied string. This might result in buffer overflow, memory corruption, and a lot of other horrible things.
I'd strongly advise that you'll use std::string instead of char* whenever possible: The default copy would have been well managed, and you don't have to care for memory allocation/deallocation,length.
C++ classes have a default assignment operator, that does a shallow copy whenever you assign one member of that class to other member unless you overload it. (except at the time of initialization, where it uses the copy constructor )
Now, coming to your code in the first part there is no overloading of the assignment operator, therefore what it does is do the shallow copy of the elements of win2 to win1 which in turn copies the 'id' and the 'title' pointer(not the string what it is pointing to(storing)).
The main function will run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will do somewhat the following
win1.id = win2.id;
win1.title = win2.title; ( note that the pointer is copied )
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
Also since win1.title and win2.title are both the same pointer
so win2.title will also point to "hello"
*/
win1.show();
win2.show();
}
But in the second part you have overloaded the assignment operator which instead of copying the pointer copies the string it is pointing to(storing).The main will now run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will now do what is in the overloaded assignment operator
where to copy the string strcpy is used which will copy the content
of the string instead of copying the pointers
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
win2.title will still point to "Default"
*/
win1.show();
win2.show();
}
Hence , the given results.
And you must also see and follow the advice given in this answer.
If Cwin does not explicitly implement its own assignment-operator (which your first example does not), the compiler will generate a default implementation that simply copies member values as-is from one object to another. That is why your output said the same thing for both objects - the win1 = win2 statement using the default assigment-operator implementation merely overwrote the pointer value of win1.title with the pointer value of win2.title, and thus both title members were left pointing at the same memory block, which the subsequent win1.set_data() statement populated with data.
To do what you are asking, you should change title to use std::string instead of char*, let it handle the copying for you. It will work fine with the compiler's default copy-constructor and assignment-operator implementations (unless you have other data that you need to make manual copies of):
#include <string>
Class Cwin
{
private:
char id;
std::string title;
public:
Cwin()
: id('D'), title("default") {}
Cwin(char i, const std::string &text)
: id(i), title(text) {}
void set_data(char i, const std::string &text)
{
id = i;
title = text;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
But, if you need to use char* directly, you must implement the copy-constructor and assignment-operator properly to ensure your title data gets copied correctly, eg:
#include <cstring>
Class Cwin
{
private:
char id, *title ;
public:
Cwin()
: id(0), title(NULL)
{
set_data('D', "default");
}
Cwin(char i, char *text)
: id(0), title(NULL)
{
set_data(i, text);
}
Cwin(const Cwin &win)
: id(0), title(NULL)
{
set_data(win.id, win.title);
}
~Cwin()
{
delete[] title;
}
Cwin& operator=(const Cwin &win)
{
if (this != &win)
set_data(win.id, win.title);
return *this;
}
void set_data(char i, char *text)
{
int len = std::strlen(text);
char *newtitle = new char[len+1];
std::strcpy(newtitle, text);
delete[] title;
title = newtitle;
id = i;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
Any time you have to manually allocate memory in a constructor and free it in a destructor, chances are good that you will also need to copy that data in a copy-constructor and assignment-operator as well. Read up about the The rule of three/five/zero. In this situation, CWin needed to follow the Rule of three in order to function properly with char*, but can follow the Rule of zero when using std::string. You should always strive to write code that follows the Rule of zero whenever possible, it makes things easier to manage.
Related
Multiple students can associate with a single Department and single student can
associate with multiple Departments, but there is no ownership between the objects
and both have their own lifecycle. Both can create and delete independently.
WAP in C++ to model the relationships.
I have implemented this code as follows
#include<iostream>
#include<cstring>
using namespace std;
class Student
{
char* name_p;
public:
Student(char *sName)
{
cout<<"Student constructor called\n";
name_p=new char(sizeof(strlen(sName)));
name_p=sName;
}
~Student()
{
cout<<"Student destructor called\n";
delete name_p;
};
char* sName()
{
return name_p;
}
};
class Department
{
char* name_p;
public:
Department(char *dName)
{
cout<<"Department destructor called\n";
name_p=new char(sizeof(strlen(dName)));
name_p=dName;
}
~Department()
{
cout<<"Department destructor called\n";
delete name_p;
}
char* dName()
{
return name_p;
}
};
class Course
{
Student* std_p;
Department* dept_p;
char* courseName_p;
static unsigned int index;
static Course *courseList_p[4];
public:
Course(char* crseName,Student* student,Department* dept)
{
cout<<"Course constructor called\n";
std_p=student;
dept_p=dept;
if(index<4)
{
courseName_p=new char(sizeof(strlen(crseName)));
courseName_p=crseName;
courseList_p[index]=this;
++index;
}
else
{
cout<<"Cannot accomodate any more Course\n";
}
};
~Course()
{
cout<<"Course destructor called\n";
delete courseName_p;
};
static char* findStudent(char *crseName, char* deptName)
{
for(int i=0; i<index; i++)
{
if ( (courseList_p[i]->getCourseName() == crseName) &&
(courseList_p[i]->getDeptName() == deptName) )
{
return(courseList_p[i]->getStdName());
}
}
}
char* getStdName()
{
return std_p->sName();
};
char* getDeptName()
{
return dept_p->dName();
};
char* getCourseName()
{
return courseName_p;
};
};
unsigned int Course::index =0;
Course* Course::courseList_p[4]={0,0,0,0};
int main()
{
int i;
cout<<"\nExample of Association class\n";
cout<<"-----------------------------------\n\n";
cout<<"We have got 4 students\n";
Student *studentNames[4] = {new Student("Meera"), new Student("Moina"), new Student("Teena"), new Student("Mridula")} ;
cout<<"\n";
cout<<"We have got 2 Departments\n";
Department *departNames[2] = {new Department("Mathematics"), new Department("ComputerSceince")} ;
cout<<"\n";
cout<<"Here class Course Associates Student and Department, with a Course name\n";
Course course1("DataStructure",studentNames[0], departNames[1]);
Course course2("Maths",studentNames[3], departNames[0]);
Course course3("Geometry",studentNames[2], departNames[0]);
Course course4("CA",studentNames[1], departNames[1]);
cout<<"\n";
cout<<"Finding a Student using Course and Department\n";
cout<<"Student who has taken Maths Course in Mathematics Department is:"<<Course::findStudent("Maths", "Mathematics")<<endl;
cout<<"\n";
cout<<"Deletion of objects\n\n";
for(i=0;i<4;i++)
{
delete studentNames[i];
}
cout<<"\n";
for(i=0;i<2;i++)
{
delete departNames[i];
}
cout<<"\n";
return 0;
}
The code is showing warnings in main function i.e. ISO C++ forbids converting a string constant to char* and also the main() function is not returning 0 but a garbage value. Please help me in rectify these errors and warnings.
Also I don't want to use this pointer in class course, can I implement the code without using this pointer.
There are a couple of problems with your code. Mostly they are related to the use of char* instead of string. I'm amazed to see that some teacher still are found of this practice.
c-string allocation problems
The first problem causes memory corruption. This could explain that your main() returns garbage instead of 0. For example:
name_p=new char(sizeof(strlen(dName))); // OUCH!!!
In the statement you allocate it wrong. If you want to allocate an array, you must use new[...]. Moreover, keep in mind that a c-string allways needs 1 char more for the trailing null. So it should be:
name_p=new char[strlen(dName)+1];
would already improve the situation.
Last but not the least, if you allocate an array you must delete an array with delete[]
c-string copying issue
Then, you can copy C++ string with an assignment operator just as in:
name_p=sName; // OUCH
However, for char*, this just overwrites the pointer that new returned and you are not sure that the c-string it points to will still be valid when it's used.
If you want to assign a c-string, you need to go for:
strcpy (name_p, sName); // C-style
copy (sName, sName+strlen(sName)+2, name_p); // Safer C++ alternative
c-string comparison issue
Suppose you have two pointers to c string, say pa and pb. Comparing the pointers looks at the memory address, not at the content. So yu could have two identical strings but the comparison could nevertheless fail. THis kind of comparison would work perfectly well for c++ string, but for cstring, you need to to
strcmp(pa, pb)==0 // instead of pa == pb
c-strings in classes and structures
Whenever you have some pointers that you allocate in a class, you need to think of the rule of 3. Meaning that you'd need to add a copy constructor and an assignment operator. Fortunately, at first sight it seems that you don't need them here. Of couse, if you'd use c++ string you wouldn't have to worry about such details.
c-string conversion issues?
ANother issue in your code is the use of string literals with constructors. For example:
new Student("Meera").... // c-string literals (constant)
Student(char*...) // constructor using a c-string pointer (non constant)
You can easily get rid of this complaint, by making the constructor's pointer argument as constant:
Student(const char *sName)...
The same applies to function arguments, such as in find.
Other problems
The static member findStudent() works well, but only if a result is found. If nothing is found, it's UB, since there is no return statement (put a return nullptr; at the end of this function?)
Moreover, the wya you use this function, directly printing its result is dangerous, because dereferencing an invalid pointer will lead to UB.
Here the corrected code. It compiles and seem to run. I didn't analyse for more issues.
I want to create a vector with diferent object of type A but ...
This code throws this exception:
free(): invalid pointer: 0x0000000000401757 ***
#include <vector>
class A
{
char * elem;
public:
A()
{
elem = new char[10];
}
void setA(char* name)
{
elem = name;
}
~A()
{
delete[] elem;
}
};
int main(int argc, char **argv)
{
std::vector<A> v_a;
for (int i = 0; i < 10; ++i)
{
A m_a;
m_a.setA("jaja");
v_a.push_back(m_a);
}
}
Why this is happening?? How can implement a correct solution?
When calling setA(...) you are not copying the data of the string, instead you are copying the address of a local string. (Check what cplusplus.com says about pointers and string literals)
After the block with the setA(...) call ends, this string goes out of scope so it's address is invalid afterwards
So you are trying to free memory from the heap in your destructor which was allocated on the stack and is long gone at this time...
Updated suggestion 1: added on Jan 27th, 2016
additionally take into account the need of a copy ctor when pushing instances of A to a std::vector. Obeying the Rule of three I added a copy assignment operator as well.
See also Rule of five in case of c++11
properly take care of possibly 'wrong' sized strings
#include <cstring>
...
/// copy ctor
A(const A& other) :
elem(new char[10])
{
// prevent duplicate code
setA(other.elem);
}
/// copy assignment operator
A& operator=(const A& other)
{
// prevent duplicate code
setA(other.elem);
return *this;
}
/// set A's name (which is truncated to 9 characters if necessary)
void setA(const char* name)
{
// copy first 9 chars from name
// if there's fewer chars: elem is padded with zeros
std::strncpy(elem, name, 9);
// add null character / zero (string delimiter) manually
// this is necessary for cases where name has 10 or more chars
elem[9] = 0;
}
(Obsolete) suggestion 1: continue working with c style strings
explicitly copy the passed string's data (you need to do this in order to make the method work)
add a length indicator to the parameter list of setA(...) (this is optional, you could also figure out the length of the string inside of the method...)
#include <cstring>
...
void setA(char* name, size_t length)
{
std::memcpy(elem, name, length);
}
Suggestion 2: switch to c++ style strings
use std::string (I'd prefer this version as it spares you the memory handling and reads very intuitively)
#include <string>
class A
{
std::string elem;
public:
A(){}
void setA(std::string name)
{
elem = name;
}
~A(){}
};
You modify the value of elem, which is a pointer to a memory area allocated on the free store. Now, elem points to a string literal, which is not applicable to delete or delete[].
deleteing objects not on the free store is undefined behavior. Your destructor attempts it nevertheless.
I saw this code on the Internet and I didn't understand it. Could anyone help me to translate it? Are the constructors defined and initialized correctly?
struct account {
string login;
string surname;
string name;
string passwd;
bool connecte;
/** accessInt :
int accessInt;
/** acl : acl */
acl* accessListe;
//constructers
account()
{
}
account(
const string & login, const string & surname,
const string & name, const string & passwd, const bool connecte , const int & accessListe
) : login(login), surname(surname), name(name), passwd(passwd), connecte(connecte), accessInt(accessListe){
accessListe = new acl(accessInt);
}
account(
const char * login, const char * surname,
const char * name, const char * passwd, const bool connecte , const int & accessListe
) : login(login), surname(surname), name(name), passwd(passwd), connecte(connecte), accessInt(accessListe){
accessListe = new acl(accessInt);
}
friend std::ostream &operator<<(std::ostream &c, const account& ac) {
c << "a:" << ac.login << ":" << ac.surname << ":" << ac.name << ":" << ac.passwd << ":" << ac.accessListe->toInt();
return c;
}
};
The code has an odd embedded comment:
/** accessInt :
int accessInt;
/** acl : acl */
acl* accessListe;
Since there is code that attempts to initialize accessInt, I assume the line above its declaration is just missing token to close the comment.
The code lacks a definition of acl.
The bodies of the latter two constructors attempt to initialize accessListe, but there is also a constructor parameter named accessListe which will shadow the object member. Either rename the parameter, or use this->accessListe in the constructor body.
jxh's answers several if not most things wrong with the code, but I'd like to extend that and talk about some others. The default constructor in particular is bad as it makes no attempt to construct any object with values. This is fine for all your std::string which are guaranteed to be constructed as an empty string; however, for your int, bool and pointer members they will all be constructed with any values. This means if the default constructor this class is called, these values will not be initialised to anything and can cause problems! Let's assume account destructor cleans up its allocated memory:
account::~account() { delete accessListe; }
If the class is constructed with its default constructor, undefined behaviour will ensue when it gets cleaned up (by default, accessListe will just point to any bit of memory). You should ensure your default constructor (and all constructors) initialise all values with normal values:
account::account() : connecte(false), accessInt(0), accessListe(nullptr /* or 0 if you don't have C++11*/) {}
Regarding the latter two constructors, both could (read: should) initialise the pointer accessListe in the constructor initialisation list, rather than assigning a value afterwards:
account::account(arguments...) : accessInt(someValue), accessListe(new acl(accessInt))
This will save an unnecessary copy.
Currently in the process of learning c++ and maybe it's because I am really frustrated right now but I really can't seem to wrap my simple little head around this:
There's a class constructor:
Class (const char* file);
I use it like this in my main class:
char* chararr = new char[2048];
//stuff with charrarr
// std::cout << chararr would be: "C:\stuff\paths\etc\"
Class c( strcat(chararr , "filename.file"));
//I want it to be: "C:\stuff\paths\etc\filename.file
Class c2( strcat(chararr , "filename2.file2"));
////I want this to be: "C:\stuff\paths\etc\filename2.file2" but it is instead
// "C:\stuff\paths\etc\filename.filefilename2.file"
The problem is that strcat modifies the chararr so the second time I do this, with Class c2, it gets all messed up... I'm guessing it's a very very basic thing to do and it gets me even more frustrated knowing I'm missing something really obvious...
Error in you code first time you should call strcpy(), whereas you are concatenates with garbage.
Class c( strcpy(chararr , "filename.file"));
else it concatenate with garbage, undefined behavior.
Edit:
// std::cout << chararr would be: "C:\stuff\paths\etc\"
size_t len = strlen(chararr); // <--- notice
Class c( strcat(chararr , "filename.file"));
// path\path2\filename.file
// ^ replace `f` with '\0'
chararr[len] = '\0'; // <--- notice
Class c2( strcat(chararr , "filename2.file2"));
Why are you using strcat when you don't need (as it seems) to concatenate strings? You could simply do:
class Class {
public:
Class(const char* file) {}
// ...
};
int main() {
Class c("filename.file");
Class c2("filename2.file2");
}
or, if you really need to concatenate strings use std::string instead of const char*.
std::string is easier to manipulate, grows in size automatically and deletes itself automatically. The only gotcha in this case, is that the class must make its own copy of the text you pass as the pointer, because when the string changes data or goes out of scope, the pointer is no longer valid.
std::string mystr = "filename.file";
MyClass c(mystr.c_str());
mystr = "filename2"; // reset string
mystr += ".file2" // concatenate
MyClass c2(mystr.c_str());
If you wrote the class, change it to use std::string as well.
class MyClass
{
public:
MyClass(const std::string& str_in)
: str(str_in) // initialization list
{
}
std::string str;
};
std::string mystr = "filename.file";
MyClass c(mystr);
mystr = "filename2"; // reset string
mystr += ".file2" // concatenate
MyClass c2(mystr);
You have to reinitialize chararr if you want to use it this way:
char* chararr = new char[2048];
strcpy(chararr,"path\\path2\\");
Class c( strcat(chararr , "filename.file"));
strcpy(chararr,"path\\path2\\");
Class c2( strcat(chararr , "filename2.file2"));
For that you're using c++ I'd recommend using std::string anyway.
I am having difficulty writing my code in the way it should be written. This is my default constructor:
Address::Address() : m_city(NULL), m_street(NULL), m_buildingNumber(0), m_apartmentNumber(0)
{}
...and this is my other constructor:
Address::Address(const char* city, const char* street, const int buildingNumber,const int apartmentNumber) : m_city(NULL), m_street(NULL)
{
SetAddress(city,street,buildingNumber,apartmentNumber);
}
I have to initialize my city and street fields as they contain char * and my setter uses remove to set a new city for example. I would very much like to hear your opinion on how to write it in the right way without repeating code.
this is my SetAddress code :
bool Address::SetAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber)
{
if (SetCity(city) == false || SetStreet(street) == false || SetBuildingNumber(buildingNumber) == false || SetApartmentNumber(apartmentNumber) == false)
return false;
return true;
}
and this is my SetCity:
bool Address::SetCity(const char* city)
{
if(city == NULL)
return false;
delete[] m_city;
m_city = new char[strlen(city)+1];
strcpy(m_city, city);
return true;
}
1 more question if i do change char* to string how can i check if string city doesnt equal to NULL as i know string does not have the "==" operator and string is an object and cannot be equal to null,
how can i check if the string i get is indeed legeal.
You should use std::string instead of C strings (const char*). Then you don't have to worry about having a "remove" function because std::string will manage the memory for you.
The only repeating code I see is the initializers. Since you should both be using initializers and cannot share initializers, some code redundancy is required here. I wouldn't worry about it.
When the new C++ comes out you'll be able to call the former constructor during initialization of the later. Until then, you'll just have to live with this minor smell.
You can combine the two ctors:
Address::Address(const char* city=NULL,
const char* street=NULL,
int buildingNumber=0,
int apartmentNumber=0)
: m_city(city),
m_street(street),
m_buildingNumber(buildingNumber),
m_apartmentNumber(apartmentNumber)
{}
[The top-level const on buildingNumber and apartmentNumber accomplished nothing and attempt to move implementation information into the interface, so I remove them.]
Of, if you really prefer:
Address::Address(const char* city=NULL,
const char* street=NULL,
int buildingNumber=0,
int apartmentNumber=0)
{
SetAddress(city,street,buildingNumber,apartmentNumber);
}
I generally prefer the former, but if SetAddress qualifies its inputs, it may be worthwhile. Of course, the suggestion to use std::string instead of pointers to char is a good one as well, but that's a more or less separate subject.
One other minor note: this does differ in one fundamental way from your original code. Your code required either 0 or 4 arguments to the ctor. This will accept anywhere from 0 to 4, arguments so a person could specify (for example) a city and street, but not a building number or apartment number. If it's really important to you that attempts at using 1, 2 or 3 arguments be rejected, this approach won't be useful to you. In this case, the extra flexibility looks like an improvement to me though -- for example, if somebody lives in a single-family dwelling, it's quite reasonable to omit an apartment number.
As answered by others (James McNellis' answer comes to mind), you should switch to std:string instead of char *.
Your problem is that repetition can't be avoided (both non default constructor and the setAddress method set the data), and having one calling the other could be less effective.
Now, the real problem, I guess, is that your code is doing a lot, which means that repetition of delicate code could be dangerous and buggy, thus your need to have one function call the other. This need can be remove by using the std::string, as it will remove the delicate code from your code altogether.
As it was not shown
Let's re-imagine your class:
class Address
{
public :
Address() ;
Address(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber) ;
// Etc.
private :
std::string m_city ;
std::string m_street ;
int m_buildingNumber ;
int m_apartmentNumber ;
} ;
Using the std::string instead of the const char * will make the std::string object responsible for handling the resource (the string itself).
For example, you'll see I wrote no destructor in the class above. This is not an error, as without a destructor, the compiler will generate its own default one, which will handle the destructor of each member variable as needed. The remove you use for resource disposal (freeing the unused char *) is useless, too, so it won't be written. This means a lot of delicate code that won't be written, and thus, won't produce bugs.
And it simplifies greatly the implementation of the constructors, or even the setAddress method :
Address::Address()
// std::string are initialized by default to an empty string ""
// so no need to mention them in the initializer list
: m_buildingNumber(0)
, m_apartmentNumber(0)
{
}
Address::Address(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber)
: m_city(p_city)
, m_street(p_street)
, m_buildingNumber(p_buildingNumber)
, m_apartmentNumber(p_apartmentNumber)
{
}
void Address::setAddress(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber)
{
m_city = p_city ;
m_street = p_street ;
m_buildingNumber = p_buildingNumber ;
m_apartmentNumber = p_apartmentNumber ;
}
Still, there is repetition in this code, and indeed, we'll have to wait C++0x to have less repetition. But at least, the repetition is trivial, and easy to follow: No dangerous and delicate code, everything is simple to write and read. Which makes your code more robust than the char * version.
Your code looks good - it might be worthy to see the contents of SetAddress. I would highly recommend using std::string over char *s, if city and street aren't hard-coded into the program, which I doubt. You'll find std::string will save you headaches with memory-management and bugs, and will generally make dealing with strings much easier.
I might rewrite the setAddress() method as follows:
bool Address::setAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber)
{
return (setCity(city)
&& setStreet(street)
&& setBuildingNumber(buildingNumber)
&& setApartmentNumber(apartmentNumber))
}
which will achieve the same short-circuiting and returning semantics, with a bit less code.
If you must use char * rather than std::string you need to manage the memory for the strings yourself. This includes copy on write when sharing the text or complete copy of the text.
Here is an example:
class Address
{
public:
Address(); // Empty constructor.
Address(const char * city,
const char * street,
const char * apt); // Full constructor.
Address(const Address& addr); // Copy constructor
virtual ~Address(); // Destructor
void set_city(const char * new_city);
void set_street(const char * new_street);
void set_apartment(const char * new_apartment);
private:
const char * m_city;
const char * m_street;
const char * m_apt;
};
Address::Address()
: m_city(0), m_street(0), m_apt(0)
{ ; }
Address::Address(const char * city,
const char * street,
const char * apt)
: m_city(0), m_street(0), m_apt(0)
{
set_city(city);
set_street(street);
set_apt(apt);
}
Address::Address(const Address& addr)
: m_city(0), m_street(0), m_apt(0)
{
set_city(addr.city);
set_street(addr.street);
set_apt(addr.apt);
}
Address::~Address()
{
delete [] m_city;
delete [] m_street;
delete [] m_apt;
}
void Address::set_city(const char * new_city)
{
delete [] m_city;
m_city = NULL;
if (new_city)
{
const size_t length = strlen(new_city);
m_city = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_city, new_city);
m_city[length] = '\0';
}
return;
}
void Address::set_street(const char * new_street)
{
delete [] m_street;
m_street = NULL;
if (new_street)
{
const size_t length = strlen(new_street);
m_street = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_street, new_street);
m_street[length] = '\0';
}
return;
}
void Address::set_apt(const char * new_apt)
{
delete [] m_apt;
m_apt = NULL;
if (new_apt)
{
const size_t length = strlen(new_apt);
m_apt = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_apt, new_apt);
m_apt[length] = '\0';
}
return;
}
In the above example, the Address instance holds copies of the given text. This prevents problems when another entity points to the same text, and modifies the text. Another common issue is when the other entity deletes the memory area. The instance still holds the pointer, but the target area is invalid.
These issues are avoided by using the std::string class. The code is much smaller and easier to maintain. Look at the above code versus some of the other answers using std::string.