Site Sponsors:
GNU C++: Ineritance & Default Parameter Confusion 
For those who like to use C++, the sole justification for many is the ability to implement a common signature over several related implementations.

Commonly known as "inheritance," when we need to use a parent's function / signature, yet provide a unique child-implementation of our own, we call it "polymorphism."

C++ Slicing


When it comes to using default parameters, we should note that those actually applied to our call will depend upon the receiving type.

No matter if we are using a vtable or no (*), this ability to "chop off" children definitions in C++ is a remnant of something called "Slicing."

By way of example, given the following base class & factory enumeration:
namespace BlogNouns {

enum Types {
typeThing, typePlace, typePerson
};

class Thing {
public:
virtual void speak(const char *psz = "Ima Thing") {
printf("Thing: %s\n", psz);
}

static Thing& Create(Types which);
};
}
, upon creating a few children we shall also provide a different default "speak" parameter:
    class Place : public Thing {
public:
virtual void speak(const char *psz = "Ima Place") {
printf("Place: %s\n", psz);
}

};

class Person : public Place {
public:
virtual void speak(const char *psz = "Ima Person") {
printf("Person: %s\n", psz);
}

Having set the stage for the first demonstration, supporting the aspersion =) we'll see:
using namespace BlogNouns;


int main(int argc, char** argv) {
puts("MyNouns");
Thing& noun = Thing::Create(Types::typePerson);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typePlace);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typeThing);
noun.speak();
delete &noun;

return 0;
}

-will use the default receiving-type ("Thing") parameter:

MyNouns
Person: Ima Thing
Place: Ima Thing
Thing: Ima Thing

Conversely however, whenever we change the receiving type to match the type actually created, we see that we will - instead - use that associated receiving-type's default parameter:

int main(int argc, char** argv) {

puts("BlogNouns");
Person& person = (Person&)Thing::Create(Types::typePerson);
person.speak();
delete &person;

Place& place = (Place&)Thing::Create(Types::typePlace);
place.speak();
delete &place;

Thing& thing = Thing::Create(Types::typeThing);
thing.speak();
delete &thing;

return 0;
}

BlogNouns
Person: Ima Person
Place: Ima Place
Thing: Ima Thing
Finally, note that I'm-a using:
c++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.



Sharing is caring,


-Rn

(*) Here is the "sliced proof" that virtual table (vtable) definitions do not matter:

With VTABLES:


/* Virtual - Replacing parental definitions.
*/

#include <iostream>

using namespace std;

namespace BlogNouns {

enum Types {
typeThing, typePlace, typePerson
};

// Simplified

class Thing {
public:
virtual void speak(const char *psz = "Ima Thing") {
printf("Thing: %s\n", psz);
}

static Thing& Create(Types which);
};

class Place : public Thing {
public:
virtual void speak(const char *psz = "Ima Place") {
printf("Place: %s\n", psz);
}

};

class Person : public Place {
public:
virtual void speak(const char *psz = "Ima Person") {
printf("Person: %s\n", psz);
}

};

Thing& Thing::Create(Types which) {
switch (which) {
case typeThing:
return *(new Thing);
case typePerson:
return *(new Person);
case typePlace:
return *(new Place);
}
}

}

using namespace BlogNouns;


int main(int argc, char** argv) {

{
puts("MyNouns");
Thing& noun = Thing::Create(Types::typePerson);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typePlace);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typeThing);
noun.speak();
delete &noun;
}
puts("BlogNouns");
Person& person = (Person&)Thing::Create(Types::typePerson);
person.speak();
delete &person;

Place& place = (Place&)Thing::Create(Types::typePlace);
place.speak();
delete &place;

Thing& thing = Thing::Create(Types::typeThing);
thing.speak();
delete &thing;

return 0;
}


Output:

MyNouns
Person: Ima Thing
Place: Ima Thing
Thing: Ima Thing
BlogNouns
Person: Ima Person
Place: Ima Place
Thing: Ima Thing


Without:


/* No Virtuals - Simply Overriding parental definitions.
*/
#include <iostream>

using namespace std;

namespace BlogNouns {

enum Types {
typeThing, typePlace, typePerson
};

// Simplified

class Thing {
public:
void speak(const char *psz = "Ima Thing") {
printf("Thing: %s\n", psz);
}

static Thing& Create(Types which);
};

class Place : public Thing {
public:
void speak(const char *psz = "Ima Place") {
printf("Place: %s\n", psz);
}

};

class Person : public Place {
public:
void speak(const char *psz = "Ima Person") {
printf("Person: %s\n", psz);
}

};

Thing& Thing::Create(Types which) {
switch (which) {
case typeThing:
return *(new Thing);
case typePerson:
return *(new Person);
case typePlace:
return *(new Place);
}
}

}

using namespace BlogNouns;


int main(int argc, char** argv) {

{
puts("MyNouns");
Thing& noun = Thing::Create(Types::typePerson);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typePlace);
noun.speak();
delete &noun;

noun = Thing::Create(Types::typeThing);
noun.speak();
delete &noun;
}
puts("BlogNouns");
Person& person = (Person&)Thing::Create(Types::typePerson);
person.speak();
delete &person;

Place& place = (Place&)Thing::Create(Types::typePlace);
place.speak();
delete &place;

Thing& thing = Thing::Create(Types::typeThing);
thing.speak();
delete &thing;

return 0;
}


Output:

Thing: Ima Thing
Thing: Ima Thing
Thing: Ima Thing
BlogNouns
Person: Ima Person
Place: Ima Place
Thing: Ima Thing

[ add comment ] ( 258 views )   |  permalink

<<First <Back | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Next> Last>>