I have a very simple class named person which is given below , I have a problem with only two functions , i.e setstring () function and setname() function , I am calling setstring() function from the setname function.
The only problem is when in the main function I write
Object.setname(“Zia”);
The result is ok as shown in the output screen,
Now when I write
Object.setname(“Zia ur Rahman”);
Nothing is displayed as you can see the output screen.
I know the problem is when I pass the name pointer to setstring () function but I am confused about it please explain it in detail that what is happening here.
#include<iostream.h>
class person
{
char* name;
public:
person();
void setname(const char*);
void setstring(const char*, char*);
void print()const;
};
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0';
}
}
void person::setname(const char* n)
{
setstring(n, name);//passing data member name
}
void person::print()const
{
cout<<"Name: "<<name<<endl;
}
main()
{
person object;
object.setname("Zia ur Rahman");
object.print();
system("pause");
}
alt text http://img264.imageshack.us/img264/8867/inheritanceimage004.jpg
alt text http://img263.imageshack.us/img263/3770/inheritanceimage003.jpg
The specific reason that nothing is being printed is that in setstring, p is copy of the name pointer, not a reference to it. Try changing the signature of setstring to:
void setstring(const char* s, char*& p);
(note the &).
See the other answers for other significant errors in the code - unless these problems are fixed, you are likely to get crashes or strange behaviour.
And unless the purpose of the code is just to learn dynamic arrays, use std::string instead :-).
If you want to do correct OO-oriented programming in C++ you should maybe stay away from direct pointer management but use the STL string class and use references instead of pointers. Then you should have an easier time and your source should produce the correct output.
Otherwise check your constructor of the person class, the name-array has just 3 elements but you're referencing the 4th one there (indices 0-2!). Also in the setstring() method you're not allocating enough space for the trailing '\0' in the array!
For starters:
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
name has three elements - you are treating it as if it had five. In a similar vein:
p=new char[strlen(s)];
should be:
p=new char[strlen(s) + 1];
In fact, that function is completely wrong - you are supposed to be copying the string into 'name', not the mysterious 'p' parameter. If you really want that function, then 'p' must be a pointer to a pointer or a reference to a pointer.
Have you thought about using std::string from STL?
Though first problem I see is this.
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
Your allocating an array of char's, the size of the array is 3 characters, then your copying "NILL" into it with strcpy so your filling in the array with all the characters but without the null terminator \0. "NILL" is a const string and such has a null terminator that is implicit but not shown for example "NILL\0". The \0 is a control character that is used to indicate the end of the string. Then you have an index out of bounds when you access the 3rd element of name array,when your size of your array is 3.
To help you find out the other parts that could be going wrong, here are some of the links to the function you use.
strcpy
strlen
strcpy will copy the whole string from one buffer to the next including the null terminator control character.
Strlen will return amount of characters between the beginning of the string and the terminating null character. Though it will not count the null character as a character. Thats why you will see suggestions with strlen(string)+1 to include the null terminator.
Though your doing well keep up the work, you will understand these little gotcha when using the standard library functions.
Suppose s have 5 chars. Then new char[strlen(s)]; allocates the memory of 5 chars. Then p[strlen(s)]='\0' is equivalent to p[5]=0. It's very very bad. p[5] does not exsist.
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0'; //<<<< LOOK! Out of range
}
}
It's rather C, than C++ code.
There is C++ equivalent:
#include <string>
#include <iostream>
class person
{
std::string name_;
public:
person();
//preferable design is to set object invariant in appropriate constructor
explicit person(const std::string &name);
std::string get_name() const;
void set_name(const std::string &name);
void print()const;
};
person::person()
: name_("NILL")
{}
person::person(const std::string &name)
: name_(name)
{}
std::string person::get_name() const
{
return name_;
}
void person::set_name(const std::string &name)
{
name_ = name;
}
void person::print()const
{
std::cout<<"Name: "<<name_<<std::endl;
}
int main()
{
person person1;
person1.set_name("Zia ur Rahman");
person1.print();
//this is better design decision
//because after constructor we have valid object
person person2("Zia ur Rahman");
person2.print();
std::cin.get();
return 0;
}
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 am new to C++. I have an exercise about constructor with const char* parameter.
class Book
{
private:
char* title;
}
public:
Book (const char* title)
{
this->title = title;
}
When I code the constructor like that, I receive error cannot convert const char to char*.
I tried using
strcpy(this->title, title);
then, it run, but I don't get the expected result.
Can anyone help me.
Thank you very much.
You are doing an exercise, did the course you are following not explain what you are supposed to do?
I would guess that this is an exercise in dynamic memory allocation and what you are expected to do is use strcpy after you have allocated some memory so that you have somewhere to copy the string to. Like this
this->title = new char[strlen(title) + 1];
strcpy(this->title, title);
You need to allocate one extra char because C style strings are terminated with an additional nul byte.
But any C++ programmer who was doing this for real (instead of it being a learning exercise) would use a std::string as paxdiablo says.
C++ doesn't let you easily change a const char * into a char * since the latter has no restrictions on how you can change the data behind that pointer.
If you're doing C++, you should be avoiding char * (legacy C) strings as much as possible. By all means take them as parameters if you must, but you should be turning them into C++ strings at the earliest opportunity:
class Book {
private:
std::string m_title;
public:
Book (const char *title) {
m_title = title;
}
};
The one thing you don't want to become is a C+ developer, that strange breed that never quite made the leap from C across to the C++ way of thinking :-)
And, actually, if a given book is never expected to change its title, you're better off making it constant and initialising it, rather than assigning to it, something like:
#include <iostream>
#include <string>
class Book {
public:
Book (const char* title): m_title(title) {};
void Dump() { std::cout << m_title << "\n"; }
private:
const std::string m_title;
};
int main() {
Book xyzzy("plugh");
xyzzy.Dump();
}
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'm new to and learning C++. I know a fair amount of Java and some C.
What I want to do is to create an immutable name class that takes in a string value, copies that string to a class field and then eventually hashes it to an ID that can be parsed much more efficiently than a string.
I'm hitting a wall due to a general lack of knowledge of C++ strings. Here's what I have so far...
#pragma once
#include <string>
class Name
{
public:
Name(std::string s);
~Name(void);
int getId();
std::string getName();
private:
int id;
std::string name;
};
and...
#include "Name.h"
Name::Name(std::string s)
{
}
So what I want to do is store the value of s, passed in by the constructor in the "name" private field. As far as I know a new string object must be created and then the value of s must be copied into it.
I also think that the argument s can and should be a string pointer instead of a string object (to prevent an unnecessary copy from occurring). If I'm right then the constructor should look like the following, right?
Name::Name(std::string &s) { ... }
In this case, nothing would need to be done special when passing in a name? IE.
Name n = new Name("Cody");
is perfectly valid? Actually I'm not sure since "Cody" to my knowledge is a constant string or something like that.
So if I'm all on the right track, then what is the proper way to actually copy the value? I'm thinking this is appropriate but I'm not sure.
#include "Name.h"
Name::Name(std::string s)
{
name = new string(s);
}
Thanks for the help in advance, I know it's a basic question but I'm slowly making baby steps into the C++ world. :) - Cody
You are close, your code can be like this after a little massage:
class Name
{
public:
Name(const std::string& s); // add const and reference
~Name(void);
int getId() cosnt; // add const
std::string getName() const; // add const
private:
int id;
std::string name;
};
Name.cpp
Name::Name(const std::string& s):name(s)
{
}
Here :name(s) is called member initializer list.
Name n = new Name("Cody"); is perfectly valid? Actually I'm not sure
since "Cody" to my knowledge is a constant string or something like
that.
No, n is not pointer, it's not like java you need to new for every object. In C++, you do
Name n("Cody");
This will call Name(const std::string& s) to initialize object n and initialize name string with "Cody".
Note: variable n has automatic storage duration, it will be destroyed if it goes out of scope.
To let n on dynamic storage duration, you need to use new/delete pair:
Name *pn = new Name("Cody");
delete pn;
or use smart pointers, you no need to call delete n_ptr; as n_ptr will be destroyed when it goes out of scope as well:
#include <memory>
std::shared_ptr<Name> n_ptr(new Name("Cody"));
EDIT:
To use Name class in other classes, it's the same way when you use string in Name class, you don't have to use pointers.
class TestName
{
public:
TestName(const Name& n):name_(n){ }
private:
Name name_;
};
TestName tn("Cody");
You should use a constant reference to std::string here.
As you said, it would prevent unnecessary copies.. But then why not just a pointer or a constant pointer?
A constant reference would allow you to pass to your function some arguments that would implicitly call the right std::string constructor.
So, in a nutshell, you could do that:
Name::Name(const std::string& s)
{
this->name = s;
}
// Or even better..
Name::Name(const std::string& s):
name(s)
{
}
int main(void)
{
Name nick("hello");
return 0;
}
You can find out about every std::string's constructors on its cplusplus.com's sheet.
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.