Cpp Chapter 12: Classes and Dynamic Memory Allocation Part2
12.3 Things to remember when using new in constructors
) If you use new in constructors, use delete in destructor. Their use should be compatible, pair new with delete and new [] with delete []
) Multiple constructors should share the same way of new -- whether all [] or all without [] (for there is only one destructor)
) Define a copy constructor
) Define an assignment operator that copies one object to another by doing deep copy( check for self-assignment, free previous memory, deep copy, return reference to invoking object)
Suppose you have code like this, which uses the String class and the standard string class:
class Magazine
{
private:
String title;
string publisher;
...
};
Do you need a copy constructor and a assignment operator for the Magazine class? No, because the default copy constructor will do memberwise copy, which uses the copy constructor of class String and string separately, so it is indeed a deep copy.
If the class involves pre-defined class objects and other members which need deep copy, you might have to access the copy constructor in String and string class explicitly
12.4 Observations about returning objects
) returning a reference to a const object
const Vector & Max(const Vector & v1, const Vector & v2)
{
if (v1.magval() > v2.magval())
return v1;
else
return v2;
}
First, returning an object invokes the copy constructor, while returning a reference don‘t. So it is a bit faster.
Second, the reference should point to an object that exists during the calling function:
const int & thing(int a, int b)
{
int temp = a+b;
return temp; // invalid! temp expired after the scope so the reference points to nothing
}
Third, both v1 and v2 in the upper example is const, so the return type has to be const to match
) returning a reference to a non-const object
Two uses: overloading the assignment operator and overloading the << operator for chained output with cout
s3 = s2 = s1
in chained assignment, "s3 = s2 = s1" is same as "s3 = s2.operator=(s1)", so the assignment operator function modifies s2 and returns it, thus leaving it non-const
) return an object
If the object being returned is local to the called function, then return the object. overloaded arithmetic operators fall into this category.
To sum up,
chart | const | non-const |
---|---|---|
reference | higher efficiency, input arguments all const | overloading assignment operator/overload << operator |
object | to avoid certain misuse | return local objects, which are defined inside methods |
12.5 Using pointers to objects
Sample code:
// saying2.cpp -- using pointers to objects
// compile with string1.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "string1.h"
const int ArSize = 10;
const int MaxLen = 81;
int main()
{
using namespace std;
String name;
cout << "Hi, what‘s your name?\n>> ";
cin >> name;
cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:\n";
String sayings[ArSize];
char temp[MaxLen];
int i;
for (i = 0; i < ArSize; i++)
{
cout << i+1 << ": ";
cin.get(temp, MaxLen);
while (cin && cin.get() != ‘\n‘)
continue;
if (!cin || temp[0] == ‘\0‘)
break;
else
sayings[i] = temp;
}
int total = i;
if (total > 0)
{
cout << "Here are your sayings:\n";
for (i = 0; i < total; i++)
cout << sayings[i] << "\n";
String * shortest = &sayings[0];
String * first = &sayings[0];
for (i = 1; i < total; i++)
{
if (sayings[i].length() < shortest->length())
shortest = &sayings[i];
if (sayings[i] < *first)
first = &sayings[i];
}
cout << "Shortest saying:\n" << * shortest << endl;
cout << "First alphabetically:\n" << * first << endl;
srand(time(0));
int choice = rand() % total;
String * favorite = new String(sayings[choice]);
cout << "My favorite saying:\n" << *favorite << endl;
delete favorite;
}
else
cout << "Not much to say, eh?\n";
cout << "Bye.\n";
return 0;
}
Noteworthy:
1 object initialization with new:
Class_name * pclass = new Class_name(value);
which invokes constructors.
2 using pointer to object:
String * shortest = &sayings[i];
Remember that access class‘s member by pointer uses the "->" opereator:
if (sayings[i].length() < shortest->length())
) Destructors are called in following situations:
1 if an object is an automatic variable, the object‘s destructor is called when the program exits the block in which the object is defined
2 if an object is a static variable, its destructor is called when the program terminates
3 if an object is created by new, its destructor is only called when explicitly delete the object
12.5.2 looking again at placement new
// placenew1.cpp -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just testing", int n = 0)
{
words = s;
number = n;
cout << words << " constructed\n";
}
~JustTesting()
{
cout << words << " destroyed\n";
}
void Show() const
{
cout << words << ", " << number << endl;
}
};
int main()
{
char * buffer = new char[BUF];
JustTesting *pc1, *pc2;
pc1 = new (buffer) JustTesting;
pc2 = new JustTesting("Heap1", 20);
cout << "Memory block addresses:\n" << "buffer: " << (void *) buffer << " heap: " << pc2 << endl;
cout << "Memory contents:\n";
cout << pc1 << ": ";
pc1->Show();
cout << pc2 << ": ";
pc2->Show();
JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
cout << "Memory contents:\n";
cout << pc3 << ": ";
pc3->Show();
cout << pc4 << ": ";
pc4->Show();
delete pc2;
delete pc4;
delete [] buffer;
cout << "Done\n";
return 0;
}
The program using placement new has 2 problems:
1 creating a second object will rewrite the first one in the buffer
solution:
pc1 = new (buffer) JustTesting;
pc3 = new (buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6);
2 freeing the buffer by "delete [] buffer" doesn‘t call the destructor of the objects containing
solution: call the destructors explicitly
pc3->~JustTesting();
pc1->~JustTesting();
Revised code:
// placenew1.cpp -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just testing", int n = 0)
{
words = s;
number = n;
cout << words << " constructed\n";
}
~JustTesting()
{
cout << words << " destroyed\n";
}
void Show() const
{
cout << words << ", " << number << endl;
}
};
int main()
{
char * buffer = new char[BUF];
JustTesting *pc1, *pc2;
pc1 = new (buffer) JustTesting;
pc2 = new JustTesting("Heap1", 20);
cout << "Memory block addresses:\n" << "buffer: " << (void *) buffer << " heap: " << pc2 << endl;
cout << "Memory contents:\n";
cout << pc1 << ": ";
pc1->Show();
cout << pc2 << ": ";
pc2->Show();
JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
cout << "Memory contents:\n";
cout << pc3 << ": ";
pc3->Show();
cout << pc4 << ": ";
pc4->Show();
delete pc2;
delete pc4;
delete [] buffer;
cout << "Done\n";
return 0;
}
Cpp Chapter 12: Classes and Dynamic Memory Allocation Part2