28
Classes with Pointer Data Members (I) Ying Wu Electrical & Computer Engineering Northwestern University [email protected] ECE230 Lectures Series

Classes with Pointer Data Members (I) Ying Wu Electrical & Computer Engineering Northwestern University [email protected] ECE230 Lectures Series

  • View
    217

  • Download
    0

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

Let’s program …

• Task: construct a class “CPerson”– Name?– Address?– Phone?

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

}

Summary

• Classes with pointer data members are special, and need more attentions!

• How to handle it?– Destructor– Copy constructor– Assignment

• Assignment function

• Overloading =

• Using this– Avoiding self-destruction

– Enabling cascading