. Copying, casting, and more. Example: MyString u Lets put our knowledge of C++ classes to use u...
-
date post
21-Dec-2015 -
Category
Documents
-
view
213 -
download
0
Transcript of . Copying, casting, and more. Example: MyString u Lets put our knowledge of C++ classes to use u...
.
Copying, casting, and more
Example: MyString
Lets put our knowledge of C++ classes to use
Define a class to represent a string Replace all the calls to strdup, strcmp, …
with methods that are clearer and handle memory allocation
[See MyString.h MyString.cpp]
Are we done yet?
Using MyString we can now write code
For example
MyString str1(“Foo”);
MyString str2(“Bar”);
…
if( str1 > str2 )
str1 = str2; // Whoa! what does this do?
…
Few Words on Copy…
What does the assignment str1 = str2 do?
High-level view: “=“ is an operator (like “+”) The commend specified here can be written(operator =)(str1,str2)
The compiler searches for a function (operator =) with arguments of type MyString
Function name Arguments
operator =
For built-in types, the language behaves as though there are functions:int& operator=(int&, int);
double& operator=(double&, double);
…
This is why we can write:int a, b;
(a = b = 5)++;
// equivalent to: b = 5; a = b; a++;
What about classes?
The same operator for a class X would have the type signatureX& X::operator=(X const& );
Why X const& argument?
To ensure that the right-hand side of copy does not change (why ref? later on)
The X& return type? To allow for x = y = z like built-in types
MyString Revisited
We never defined MyString::operator= Why our example compiles?
The compiler defines default instantiation of operator=
This happens for every class for which the programmer did not define his own operator=
Saves unneeded work for many classes
Default operator=
The default operator= copies all data members of the class
In our case, the result isMyString&
MyString::operator=(MyString const& rhs)
{
m_length = rhs.m_length;
m_string = rhs.m_string;
return *this; // return reference to
// current object
}
Example Revisited
void boringExample()
{
MyString str1(“Foo”);
MyString str2(“Bar”);
if( str1 > str2 )
str1 = str2;
}
Problem! A memory location is deleted twice Memory leak
m_stringstr1:
m_stringstr2:
“Foo”
“Bar”
str1 constructorstr2 constructorstr1.operator=(str2)str1 destructorstr2 destructor
The fix?
Define our own operator=MyString&
MyString::operator=(MyString const& rhs)
{
delete [] m_string;
m_length = rhs.m_length;
init( rhs.m_string );
return *this;
}
Is This Solution Water Tight?
What if a programmer writesMyString str(“foo”);
…
str = str; // senseless, but legal!
What happens? delete str.m_string allocate a new str.m_string copy this string onto itself…
Checking for Self-copy
Add another check at the beginning of the procedure
MyString&
MyString::operator=(MyString const& rhs)
{
if( this == &rhs )
return *this;
…
Are We Out of The Woods?
Consider the following codevoid doNothing( MyString S )
{
}
void anotherBoringExample()
{
MyString str(“foo”);
doNothing(str);
}
What is wrong with this picture?
Copy Constructor
What happens when we call DoNothing(str) ? A new MyString object is created on the stack This object needs to be constructed It also needs to copy the value of str
Copy constructor:
MyString::MyString(MyString const& init);
Copy Constructor This constructor is also called when initializing
a new object
MyString str2 = str1;
is equivalent to writing
MyString str2(str1);
In both cases the compiler calls copy constructor
Default Copy Construct
Same as with operator= If not specified, default instantiation is by
copying all data members Constructing each one with the value of the
matching field in source
In our example:
MyString::MyString(MyString const& rhs)
: m_length( rhs.m_length ),
m_string( rhs.m_string )
{}
Example Revisited
void doNothing( MyString S )
{
}
void anotherBoringExample()
{
MyString str(“foo”);
doNothing(str);
}
Problem! str.m_string is deleted by the destructor
Fix?
MyString::MyString(MyString const& rhs)
{
m_length = rhs.m_length;
init( rhs.m_string );
}
Lessons
If a class manages memory, then Define you own operator= and copy
constructor Remember to copy all data members Make sure to check for self-copy Remember that operator= returns a
reference to the object
Disallowing Copy
Some times we want to create classes in which object cannot be copied Large objects, such as a database “One time” data structure …
How do we ensure that object from this class are not copied?
Disallowing Copy
Solution #1: Do not define operator= and copy constructor
Problem: Compiler will define default versions This is not what we want…
Disallowing Copy
Solution #2: generate runtime error
X& operator=(X const& x )
{
assert(false);
return *this;
}
Caveat: Problems shows up very late in the
development process…
Disallowing Copy
Solution #3: Define operator= as privateclass X {
…
private:
X& operator=(X const&);
};
Cannot be called (compilation error) from outside methods of X
Copy & Inheritance
class Base {
…
private:
double m_x;
int m_a;
};
class Derived
: public Base {
…
private:
double m_z;
};
Base a;
Derived b;
…
a = b;
default copy will
m_x
m_a
a:
m_x
m_a
m_z
b:
Copy & Inheritanceclass A { public: virtual void f1(); virtual void f2(); int m_a;};
class B: public A { public: virtual void f1(); virtual void f3(); void f4();
int m_b;};… A a; B b; a = b;
class A { public: virtual void f1(); virtual void f2(); int m_a;};
class B: public A { public: virtual void f1(); virtual void f3(); void f4();
int m_b;};… A a; B b; a = b;
<vtbl>
m_a
m_b
b:<vtbl>
m_a
a1:
VTBLs
f1
f2
f3
f1
f2
A
B
Function Resolution
With overloading, we can have several functions with the same name
How does C++ know which function to call?
Example
int square( int x );double square( double x );
In C, we get error: “conflicting types for ‘square’”
There can only be one function called square
In C++ these lines do not cause an error The two functions are considered different
declarations
Function Resolution
How does C++ determine which function to call?
Lets check [resolution.cpp]
Function Resolution
In general, upon seeing the statement
foo(x);
The compiler searches for a function foo with argument types that match x
or a function foo with argument types that can be
casted to from the type of x
Function Resolution
int square( int x );
double square( int x );
This does not compile, why? Based on return type alone, we cannot
distinguish between the functions. It is possible that we will encounter code like
…
square( 5 );
…
Function Resolution
How does C++ determine whether it can cast type A to type B ?
Standard Casts: int double, char int, etc.
Tailored Casts: casts introduced by the programmer
Example
class MyClass {
public:
MyClass(int i);
…
};
The constructor is viewed as a way of taking an integer an making it into a MyClass object
Example
void foo( MyClass x );
…
int i;
…
foo(i); // What happens here?
The compiled code for the last line is eq. to
{
MyClass tmp(i);
foo(tmp);
}
Implicit Cast
This feature is useful for seamless operations For example, MyString has a cast from char* We can use “<String>” in calling functions
that expect MyString
MyString str;
…
if( str == “foo” )
…
Implicit Castclass IntArray {
public:
IntArray(int size); // constructor
bool operator==(IntArray const& rhs) const;
int operator[](int i) const;
…
};
…
IntArray A(5), B(5);
…
for( i = 0; i < 5; i++ )
if( A == B[i] ) // oops! should have been A[i] == B[i]
…
This creates a new temporary IntArray of size B[i] !!!
Explicit Keyword
A directive to compiler not to use constructor in implicit casts
class IntArray {
public:
explicit IntArray(int size); // constructor
…
};
Lessons
C++ can create temporary objects “behinds the scenes”
To avoid this: Use explicit keyword for constructors Pass objects by reference
void foo( MyClass const& Obj );
instead ofvoid foo( MyClass Obj );
Declare a private copy constructor