Show Lecture.DefaultMethods as a slide show.
CS253 Default Methods
a couple of poor default avatars
The word
The word “default” has several meanings in C++:
- A ctor with no arguments is the default constructor.
This code calls it by default:
string s; // no parens, but ctor still called
cout << s.size() << ' ' << s.capacity() << ' ' << sizeof(s);
0 15 32
- A compiler-written method is called a default method. Many
classes have a default dtor that exists, but does nothing.
- This implies the existence of a default default ctor.
- The
= default
syntax can be used to stress that we want the
default implementation of a method: ~Foo() = default;
- A
default:
case in a switch statement is when none of the
other cases match.
Simple Example
class Foo {
public:
Foo(const string &n) : name(n) { }
string name;
};
Foo a("Diane"), b(a);
cout << a.name << ' ' << b.name << '\n';
Diane Diane
There’s no copy ctor, so who set b.name
to Diane
?
There is a default copy ctor, which performs a bitwise copy
on the object.
No
NO!
I Lied
- The default copy ctor does not do a bitwise copy.
- The default copy ctor does a memberwise copy.
- You can’t just bitwise copy a C++ object.
- What if it contains pointers to dynamically-allocated data,
or a handle to an opened file?
- A bitwise copy is a shallow copy;
we almost always want a deep copy.
- Do a memberwise copy by copying each member datum according
to its copy ctor. An int will get bitwise copied,
but the copy ctor of a map will allocate a bunch of nodes
and copy data.
Like this
The default (compiler-generated) copy ctor & assignment operators
look like this:
class Foo {
public:
Foo(const string &n) : name(n) { }
Foo(const Foo &rhs) : name(rhs.name) { }
Foo& operator=(const Foo &rhs) {
name = rhs.name;
return *this;
}
string name;
};
Foo a("Diane"), b(a);
b = a;
cout << a.name << ' ' << b.name << '\n';
Diane Diane
Missing
- If you write any constructor, then the system will not provide
a default constructor.
- However, you still get the default copy ctor and assignment operator.
- You always get a default destructor.
- Don’t depend on defaults; make your intentions clear:
class Foo {
public:
Foo() = default; // I love this one.
Foo(const Foo &) = delete; // Not wild about this one.
};
Inheritance
Great! That explains everything!
Wait—what about inheritance?
Inheritance Example
struct Base { // struct ⇒ public access
string name;
};
struct Derived : public Base {
Derived(const string &n) { name = n; }
};
Derived a("Dorothy"), b(a);
cout << a.name << ' ' << b.name << '\n';
Dorothy Dorothy
How did that work‽ Who copied name
? Sure, the Base
default
copy ctor would copy name
, but nobody called that.
Sure Did
- In fact, the
Derived
copy ctor called the Base
copy ctor.
- The base class is, in some kind of ways, considered to be a member
of the derived class.
- Therefore, the default copy ctor & assignment operators
also copy the base class.
Code with Default Methods
struct Base {
Base() { }
Base(const Base &rhs) : name(rhs.name) { }
Base& operator=(const Base &rhs) {
name = rhs.name;
return *this;
}
string name;
};
struct Derived : public Base {
Derived(const string &n) { name = n; }
Derived(const Derived &rhs) : Base(rhs) { }
Derived& operator=(const Derived &rhs) {
Base::operator=(rhs);
return *this;
}
};
Derived a("Dorothy"), b(a);
cout << a.name << ' ' << b.name << '\n';
Dorothy Dorothy