denoted with the keyword const after a function declaration, makes it a compiler error for this class function to change a member variable of the class
mutable
To allow some of the variables to be writable even when the function is marked as a "const function", these class variables are marked with the keyword mutable
// ----Virtual Functions
class Animal
{
public:
void eat() { std::cout << "I'm eating generic food."; }
}
class Cat : public Animal
{
public:
void eat() { std::cout << "I'm eating a rat."; }
}
Animal *animal = new Animal;
Cat *cat = new Cat;
animal->eat(); // outputs: "I'm eating generic food."
cat->eat(); // outputs: "I'm eating a rat."
//this can go at the top of the main.cpp file
void func(Animal *xyz) { xyz->eat(); }
Animal *animal = new Animal;
Cat *cat = new Cat;
func(animal); // outputs: "I'm eating generic food."
func(cat); // outputs: "I'm eating generic food."
//The solution is to make eat() a virtual function:
class Animal
{
public:
virtual void eat() { std::cout << "I'm eating generic food."; }
}
class Cat : public Animal
{
public:
void eat() { std::cout << "I'm eating a rat."; }
}
func(animal); // outputs: "I'm eating generic food."
func(cat); // outputs: "I'm eating a rat."
//a straightforward demonstration
Animal *animal = new Animal;
Animal *cat = new Cat; animal->eat(); // outputs: "I'm eating generic food."
cat->eat(); // outputs: "I'm eating generic food."
//Even though you're assigning the subclassed object (Cat), the method being invoked is based on the pointer type (Animal) not the type of object it is point to. This is why you need "virtual"
// ----virtual inheritance
The diamond problem
http://www.geeksforgeeks.org/multiple-inheritance-in-c/
http://www.cprogramming.com/tutorial/virtual_inheritance.html
The diamond problem occurs when two superclasses of a class have a common base class.
Since both transmitter and receiver classes are using the method write() from the base class, when calling the method write() from a radio object the call is ambiguous; the compiler can't know which implementation of write() to use, the one from the transmitter class or the one from the receiver class.
To understand how this is works, let's take a look at how the objects are represented in memory. Inheritance simply puts the implementation of two objects one after another, but in this case radio is both a transmitter and a receiver, so the storable class gets duplicated inside the radio object. The g++ compiler will complain when compiling the code: error: 'request for member "write" is ambiguous', because it can't figure out whether to call the method write() fromstorable::receiver::radio or from storable::transmitter::radio.
C++ allows us to solve this problem by using virtual inheritance. In order to prevent the compiler from giving an error we use the keyword virtual when we inherit from the base class storable in both derived classes:
Since both transmitter and receiver classes are using the method write() from the base class, when calling the method write() from a radio object the call is ambiguous; the compiler can't know which implementation of write() to use, the one from the transmitter class or the one from the receiver class.
class storable //this is the our base class inherited by transmitter and receiver classes
{ public: storable(const char*);
virtual void read();
virtual void write();
virtual ~storable();
private: ....
}
class transmitter: public storable
class transmitter: public storable
{ public: void read(); ... }
class receiver: public storable
{ public: void read(); ... }
class radio: public transmitter, public receiver
{ public: void read(); .... }
To understand how this is works, let's take a look at how the objects are represented in memory. Inheritance simply puts the implementation of two objects one after another, but in this case radio is both a transmitter and a receiver, so the storable class gets duplicated inside the radio object. The g++ compiler will complain when compiling the code: error: 'request for member "write" is ambiguous', because it can't figure out whether to call the method write() fromstorable::receiver::radio or from storable::transmitter::radio.
class storable //this is the our base class inherited by transmitter and receiver classes
{
public:
storable(const char*);
virtual void read();
virtual void write();
virtual ~storable();
private:
....
}
class transmitter: public virtual storable
class transmitter: public virtual storable
{
public:
void read();
...
}
class receiver: public virtual storable
{
public:
void read();
...
}
class radio: public transmitter, public receiver
{ public: void read(); .... }
Memory Layout in Virtual Inheritance
//cross delegation:
//A powerful technique that arises from using virtual inheritance is to delegate a method from a class in another class by using a common abstract base class. This is also called cross delegation. Let's assume we have a similar scenario like in the diamond example, with small changes. Suppose the write() method in transmitter class needs to access the read() method from receiver for the radio to work (this is kind of a weird behavior, but let's take it for the sake of illustration) :
class storable
{
public:
storable(const char*);
virtual void read()=0; //this becomes pure virtual making storable an abstract
virtual void write(); //class
virtual ~storable();
private:
....
}
class transmitter: public virtual storable
{
public:
void write()
{
read();
....
}
}
class receiver: public virtual storable
{
public:
void read();
}
class radio: public transmitter, public receiver
{
public:
...
}
int main()
{
radio *rad = new radio();
receiver *r1 = rad;
transmitter *r2 =rad;
rad->write();
r1->write();
r2->write();
return 1;
}
Because of virtual inheritance, when the write() function from the transmitter class is called, the method read() from the receiver class gets called (as you may have noticed, the transmitter class doesn't have a read() function). In the above hierarchy we can instantiate only the radio class because transmitter and receiver are abstract due to virtual inheritance.
// ----Returning by reference or const reference
has no speed difference - both are very fast as they just return a reference to the original object, no copying is involved.
if you return by const reference, it binds you to always keep an instance of std::string in your class to back up the reference. That is, even if you later want to redesign your class so that it computes the string on the fly in the getter instead of storing it internally, you can't. You'd have to change your public interface at the same time, which can break code using the class.
// ----Protected constructor
When a class is (intended as) an abstract class, a protected constructor is exactly right. In that situation you don't want objects to be instantiated from the class but only use it to inherit from. There are other uses cases, like when a certain set of construction parameters should be limited to derived classes.
// ----Private constructor
Private constructor means a user cannot directly instantiate a class. Instead, you can create objects using something like the Named Constructor Idiom, where you have static class functions that can create and return instances of a class.
Named Constructor Idiom: https://isocpp.org/wiki/faq/ctors#named-ctor-idiom
A technique that provides more intuitive and/or safer construction operations for users of your class
The problem is that constructors always have the same name as the class. Therefore the only way to differentiate between the various constructors of a class is by the parameter list. But if there are lots of constructors, the differences between them become somewhat subtle and error prone.
class Point {
public:
Point(float x, float y); // Rectangular coordinates
Point(float r, float a); // Polar coordinates (radius and angle)
// ERROR: Overload is Ambiguous: Point::Point(float,float)
};
int main()
{
Point p = Point(5.7, 1.2); // Ambiguous: Which coordinate system?
// ...
}
One way to solve this ambiguity is to use the Named Constructor Idiom:
#include <cmath> // To get std::sin() and std::cos()
class Point {
public:
static Point rectangular(float x, float y); // Rectangular coord's
static Point polar(float radius, float angle); // Polar coordinates
// These static methods are the so-called "named constructors"
// ...
private:
Point(float x, float y); // Rectangular coordinates
float x_, y_;
};
inline Point::Point(float x, float y)
: x_(x), y_(y) { }
inline Point Point::rectangular(float x, float y)
{ return Point(x, y); }
inline Point Point::polar(float radius, float angle)
{ return Point(radius*std::cos(angle), radius*std::sin(angle)); }
Now the users of Point have a clear and unambiguous syntax for creating Points in either coordinate system:
int main()
{
Point p1 = Point::rectangular(5.7, 1.2); // Obviously rectangular
Point p2 = Point::polar(5.7, 1.2); // Obviously polar
// ...
}
In order to keep track of the single instance of the storable object, the compiler will provide a virtual function table (vtable) for classes transmitter and receiver. When a radio object is constructed, it creates one storable instance, a transmitter instance and a receiver instance. The transmitter and receiver classes have a virtual pointer in their vtables that stores the offset to the storable class. When the transmitter class or the receiver class goes to access any fields of the storable, it uses the virtual pointer in its vtable to find the storable object and find the field in it. This tutorial offers a comprehensive explanation of the memory layout of virtual inheritance in GCC.
//cross delegation:
//A powerful technique that arises from using virtual inheritance is to delegate a method from a class in another class by using a common abstract base class. This is also called cross delegation. Let's assume we have a similar scenario like in the diamond example, with small changes. Suppose the write() method in transmitter class needs to access the read() method from receiver for the radio to work (this is kind of a weird behavior, but let's take it for the sake of illustration) :
class storable
{
public:
storable(const char*);
virtual void read()=0; //this becomes pure virtual making storable an abstract
virtual void write(); //class
virtual ~storable();
private:
....
}
class transmitter: public virtual storable
{
public:
void write()
{
read();
....
}
}
class receiver: public virtual storable
{
public:
void read();
}
class radio: public transmitter, public receiver
{
public:
...
}
int main()
{
radio *rad = new radio();
receiver *r1 = rad;
transmitter *r2 =rad;
rad->write();
r1->write();
r2->write();
return 1;
}
Because of virtual inheritance, when the write() function from the transmitter class is called, the method read() from the receiver class gets called (as you may have noticed, the transmitter class doesn't have a read() function). In the above hierarchy we can instantiate only the radio class because transmitter and receiver are abstract due to virtual inheritance.
// ----Returning by reference or const reference
has no speed difference - both are very fast as they just return a reference to the original object, no copying is involved.
if you return by const reference, it binds you to always keep an instance of std::string in your class to back up the reference. That is, even if you later want to redesign your class so that it computes the string on the fly in the getter instead of storing it internally, you can't. You'd have to change your public interface at the same time, which can break code using the class.
// ----Protected constructor
When a class is (intended as) an abstract class, a protected constructor is exactly right. In that situation you don't want objects to be instantiated from the class but only use it to inherit from. There are other uses cases, like when a certain set of construction parameters should be limited to derived classes.
// ----Private constructor
Private constructor means a user cannot directly instantiate a class. Instead, you can create objects using something like the Named Constructor Idiom, where you have static class functions that can create and return instances of a class.
Named Constructor Idiom: https://isocpp.org/wiki/faq/ctors#named-ctor-idiom
A technique that provides more intuitive and/or safer construction operations for users of your class
The problem is that constructors always have the same name as the class. Therefore the only way to differentiate between the various constructors of a class is by the parameter list. But if there are lots of constructors, the differences between them become somewhat subtle and error prone.
class Point {
public:
Point(float x, float y); // Rectangular coordinates
Point(float r, float a); // Polar coordinates (radius and angle)
// ERROR: Overload is Ambiguous: Point::Point(float,float)
};
int main()
{
Point p = Point(5.7, 1.2); // Ambiguous: Which coordinate system?
// ...
}
One way to solve this ambiguity is to use the Named Constructor Idiom:
#include <cmath> // To get std::sin() and std::cos()
class Point {
public:
static Point rectangular(float x, float y); // Rectangular coord's
static Point polar(float radius, float angle); // Polar coordinates
// These static methods are the so-called "named constructors"
// ...
private:
Point(float x, float y); // Rectangular coordinates
float x_, y_;
};
inline Point::Point(float x, float y)
: x_(x), y_(y) { }
inline Point Point::rectangular(float x, float y)
{ return Point(x, y); }
inline Point Point::polar(float radius, float angle)
{ return Point(radius*std::cos(angle), radius*std::sin(angle)); }
Now the users of Point have a clear and unambiguous syntax for creating Points in either coordinate system:
int main()
{
Point p1 = Point::rectangular(5.7, 1.2); // Obviously rectangular
Point p2 = Point::polar(5.7, 1.2); // Obviously polar
// ...
}