virtual
in Base Class
virtual
Functions in Constructors/Destructors
*this
*this = *this
C++ fills in default implementations for you if you don’t write anything. For example, an empty class:
class Foo{};
Will be equivalent to:
class Foo{
public:
Foo(){}; // Calls each member's default constructor
Foo(const Foo& f){...}; // Calls each member's copy constructors
Foo& operator=(const Foo& rhs){...}; // Calls each member's = operator
};
C++ has these default implementations but sometimes it’s just impossible. For example:
class Foo{
std::string& s; // cannot change reference
const int x; // cannot change const
};
If we wanted to call the assignment operator of Foo
, as in f2 = f1;
, it would be illegal. There would be a compiler error in this case.
Achieve linker error upon copy using:
class Foo{
private:
Foo(const Foo&); // we don't need a var name
Foo& operator=(const Foo&); // this neither
};
Privatize the constructor too:
class Foo{
public:
// 'static' means you don't need
// an instance to call this getFoo()
static Foo& getFoo(){
// static variable. Contrary to
// intuition, this only gets initialized
// once ever. Any subsequent calls will
// NOT create any new instances.
static Foo foo;
return foo;
};
Foo(const Foo&) = delete;
void operator=(const Foo&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
private:
Foo(){}
Foo(const Foo&);
Foo& operator=(const Foo&);
};
virtual
in Base ClassIf you have a base class, Base
, and a derived class, Derived
, and you use polymorphism like:
Base* derived = new Derived(); // polymorphic pointer
delete derived; // UH OH!
If you don’t have virtual
, then the vtable will not be initialized. This means Base
portion of derived
will be deleted, but there will be UB in general for whatever is left. Memory leaks ensue.
class Base{
public:
...
virtual ~Base(); // Important virtual!
};
Sometimes, people try to derive the stl library in C++:
// DON'T DO THIS
class DerivedString : public std::string{
...
};
stl library is not implemented with inheritance in mind, so don’t do this. In case you have polymorphic designs, you will only be able to delete part of the object via deleting the std::string
portion.
public/protected/private
inheritanceFrom a very intuitive stack overflow answer:
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
Usually, you shouldn’t be getting one in the first place. Having exceptions inside destructors is really dangerous because:
{
std::vector<Foo> v; // suppose Foo has a destructor that throws
} // scope leaves, destructor called on all Foo's
// ...
// BAM, 10 exceptions at once, undefined behavior!
C++ apparently can only handle one active exception at once. So how should we handle this?
close()
function:{
Foo foo; // suppose Foo has a destructor that throws
...
try{
foo.close(); // close it ourselves
} catch (std::exception& e){
std::cout << "Error : " << e.what() << std::endl;
}
}
Now, if the user doesn’t respect this close()
function, then they’re asking for us to kill their program or absorb the error. It wouldn’t be surprising if they didn’t listen:
Foo::~Foo(){
...
catch (bad){
log.write("Error occured. Aborting.");
std::abort(); // kill it with fire
}
}
virtual
Functions in Constructors/DestructorsWhen you call virtual
inside of a constructor, hoping it would reference the derived class’s implementation, you are making a huge mistake.
Why? Because when you call a constructor, you first begin by creating the base class. Before the base class is fully constructed, you cannot possibly have a notion of derived class.
Calling virtual
will refer to the base class’s implementation, which is NOT what you want!
class Base{
public:
Base(){
foo(); // Error - virtual!
}
virtual void foo() = 0;
}
Sometimes, a compiler error won’t even show up due to indirection:
class Base{
public:
Base(){
bar(); // No error - not a virtual!
}
void bar(){
foo();
};
virtual void foo() = 0;
}
Which is extremely tricky.
You can call the base class’s explicit
function using a helper function to help shape your input first:
class Base{
public:
Base(const std::string& s){
log(s);
};
private:
void log(const std::string& s){
...
};
};
*this
For all operators, +, -, /, *, +=, -=, =
, just return *this
at the end of the function:
Foo& operator+=(const Foo& rhs){
...
return *this; // do this or else won't compile sometimes!
}
*this = *this
Suppose we had a class:
class Foo{
public:
...
Foo& operator=(const Foo& rhs){
...
delete s; // deleting itself on both rhs and lhs :(
s = new char[5];
}
private:
char* s;
};
Foo f1;
Foo* fp1 = &f1; // secretly the same object!
Foo* fp2 = &f1;
*fp1 = *fp2; // uh oh...
So prevent this by a simple pointer check:
if (&rhs == this) return *this;
You may think that:
class Derived : Base{
public:
Derived(const Derived& rhs) : x(rhs.x){};
private:
int x;
};
is sufficient for copying. It’s not.
You copied all the parts of the Derived
class, but none of the Base
. You called the default constructor of Base
in this case, and so you won’t have any of its values.
Always initialize the base class in copy ctor:
class Derived : Base{
public:
Derived(const Derive& rhs) : Base(rhs), x(rhs.x){};
private:
int x;
};