View
217
Download
0
Tags:
Embed Size (px)
Citation preview
Classes with Pointer Data Members (I)
Ying Wu Electrical & Computer Engineering
Northwestern [email protected]
ECE230 Lectures Series
Warming up…
char *str_dup_new(const char *str)
{
return ( strcpy(new char [strlen(str)+1], str) );
}
char *strcpy( char *strDestination, const char *strSource );
Example: char string[80];
strcpy( string, "Hello world from " );
size_t strlen( const char *string );
Example: void main( void ) {
char buffer[61] = "How long am I?";
int len = strlen( buffer );
cout << buff << “ is “ << len << “characters long\n";
}
Output: 'How long am I?' is 14 characters long
1st versionclass CPerson{
public:
CPerson();
CPerson(const char* n, const char*a, const char* p);
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
private:
char *m_name;
char *m_address;
char *m_phone;
};
Is it a good version?
CPerson::CPerson()
{
m_name = NULL;
m_address = NULL;
m_phone = NULL;
}
CPerson::CPerson(const char* n, const char* a, const char *p)
{
set_name(n);
set_address(a);
set_phone(p);
}
What to learn today?
• Why they are troublesome?
• Troubles and Solutions– destructor– Assignment operation– The this pointer– The copy constructor: initialization vs. assignment
What are special?• The class contains pointer data members• We have to take care of the memory those pointer data
members pointing to– i.e., using dynamic memory management– allocate memory for these pointer data members when
instantiate each object– deallocate memory back when destroy objects
• Where does the trouble come?– initialization– Assignment– copying
• Special care has to be taken!
A destructor is neededCPerson::~CPerson()
{
delete [] m_name;
delete [] m_address;
delete [] m_phone;
}
void main(){
CPerson kk(“Karel”, “Evanston”, “847 111 1111”);
CPerson *bb = new CPerson(“Bill Clinton”, “DC”, “800 222 222”);
cout << kk.get_name()<< kk.get_address() << kk.get_phone() << endl;
cout << bb->get_name() << bb->get_address() << bb->get_phone();
delete bb;
}
So, this is the 2nd versionclass CPerson{
public:
CPerson();
CPerson(const char* n, const char* a, const char* p);
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
private:
char *m_name;
char *m_address;
char *m_phone;
};
Trouble 1: Call-by-Valuevoid do_nothing(CPerson p)
{
// I actually do nothing here
// but, unfortunately, I am in trouble!
}
m_name
m_address
m_phone
mike object p
Call-by-value
m_name
m_address
m_phone
mike object p
After destruction of p
m_name
m_address
m_phone
mike object p
Before function call ???
void main()
{
CPerson mike(“Mike”, “Evanston”,”1111”);
do_nothing(mike);
}
Wild Pointer
• The deallocated memory will likely become occupied during subsequent memory allocations
• But the pointer members are still pointing to those memory locations
• Therefore, these pointer members become “wild”• We can not track these pointer any more• Wild pointers are very hard to trace and debug!• So, try to avoid them when coding
Trouble 2: Return-by-ValueCPerson test()
{
CPerson mike(“mike”,”IL”,”1111”);
return mike;
}
m_name
m_address
m_phone
mike object tmp
Return-by-value
m_name
m_address
m_phone
tmp mike
After destruction of mike
m_name
m_address
m_phone
mike object tmp
Before return ???
void main()
{
CPerson t;
t = test();
}
3rd Version: Copy Constructorclass CPerson{
public:
CPerson();
CPerson(const char*n, const char* a, const char* p);
CPerson(const CPerson& person); // copy constructor
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
private:
char *m_name;
char *m_address;
char *m_phone;
};
3rd Version: Copy ConstructorCPerson::CPerson(const CPerson & person)
{
set_name(person.get_name());
set_address(person.get_address());
set_phone(person.get_phone());
}
Trouble still…wild pointervoid print_person(const CPerson &p)
{
CPerson tmp;
tmp = p; // assignment operator
cout << tmp.get_name() << tmp.get_address() << tmp.get_phone() << endl;
}
m_name
m_address
m_phone
object p object tmp
After the assignment
m_name
m_address
m_phone
object p object tmp
After destruction of tmp
m_name
m_address
m_phone
object p object tmp
Before the assignment ???
Solution to Assignment
object tmp
Before the assignment ???
m_name
m_address
m_phone
object p
After the assignment
m_name
m_address
m_phone
object p object tmpm_name
m_address
m_phone
After destruction of tmp
m_name
m_address
m_phone
object p object tmpm_name
m_address
m_phone
4th Version: Assignmentclass CPerson{
public:
CPerson();
CPerson(const char*n, const char* a, const char* p);
CPerson(const CPerson& person); // copy constructor
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
void assign(const CPerson &p); // assignment function
private:
char *m_name;
char *m_address;
char *m_phone;
};
4th version: Assignmentvoid CPerson::assign(const CPerson &p)
{
// delete our own previously used memory
delete [] m_name;
delete [] m_address;
delete [] m_phone;
// now copy the new data passed
m_name = str_dup_new(p.get_name());
m_address = str_dup_new(p.get_address());
m_phone = str_dup_new(p.get_phone());
}
void print_person(const CPerson &p)
{
CPerson tmp;
tmp.assign(p);
cout << tmp.get_name() << tmp.get_address() << tmp.get_phone() << endl;
}
What if I want = ?
void main()
{
CPerson mike(“Michael”, “Evanston”, “847 111 1111”);
CPerson tmp;
tmp = mike;
tmp.print_person();
}
5th Version: Elegantclass CPerson{
public:
CPerson();
CPerson(const char*n, const char* a, const char* p);
CPerson(const CPerson &person); // copy constructor
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
void assign(const CPerson &p);
void operator=(const CPerson &p); // overloading =
private:
char *m_name;
char *m_address;
char *m_phone;
};
5th version (cont.)void CPerson::operator=(const CPerson &p)
{
// delete our own previously used memory
delete [] m_name;
delete [] m_address;
delete [] m_phone;
// now copy the new data passed
m_name = str_dup_new(p.get_name());
m_address = str_dup_new(p.get_address());
m_phone = str_dup_new(p.get_phone());
}
Potential trouble… self-destruction
void trouble(const CPerson &p)
{
p = p; // auto-assignment
}
When an object is assigned to itself, i.e., auto-assignment, a trouble occurs: the allocated strings of the receiving objects are first released, but this also leads to the release of the strings of the right-hand side variable, which is called “self-destruction”.
6th Version: thisclass CPerson{
public:
CPerson();
CPerson(const char*n, const char* a, const char* p);
CPerson(const CPerson &person); // copy constructor
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
void assign(const CPerson &p);
void operator=(const CPerson &p); // overloading =
private:
char *m_name;
char *m_address;
char *m_phone;
};
6th version: using thisvoid CPerson::operator=(const CPerson &p)
{
if (this != &p){
// delete our own previously used memory
delete [] m_name;
delete [] m_address;
delete [] m_phone;
// now copy the new data passed
m_name = str_dup_new(p.get_name());
m_address = str_dup_new(p.get_address());
m_phone = str_dup_new(p.get_phone());
}
}
What if I want …
void main()
{
CPerson mike(“Michael”, “Evanston”, “847 111 1111”);
CPerson a, b;
a = b = mike;
a.print_person();
}
7th Version: Cascadingclass CPerson{
public:
CPerson();
CPerson(const char*n, const char* a, const char* p);
CPerson(const CPerson &person); // copy constructor
~CPerson();
void set_name(const char *n) { m_name = str_dup_new(n); };
void set_address(const char *a) { m_address = str_dup_new(a); };
void set_phone(const char *p) { m_phone = str_dup_new(p); };
const char* get_name() const { return m_name; };
const char* get_address() const { return m_address; };
const char* get_phone() const { return m_phone; };
void assign(const CPerson &p);
const CPerson& operator=(const CPerson &p); // enable cascading
private:
char *m_name;
char *m_address;
char *m_phone;
};
7th version (cont.)
const CPerson& CPerson::operator=(const CPerson &p)
{
if (this != &p){
delete [] m_name;
delete [] m_address;
delete [] m_phone;
m_name = str_dup_new(p.get_name());
m_address = str_dup_new(p.get_address());
m_phone = str_dup_new(p.get_phone());
}
return (*this); // enable cascading
}