I have 1 question because I am pretty curious how to handle with such problem.
I have base class called "Pracownik" (Worker) and 2 subclasses which are made from public Pracownik;
- Informatyk (Informatic)
- Księgowy (Accountant)
Writing classes is easy. Made them pretty fast but I have small problem with main because I am helping friend with program but I was not using C++ for a while. So:
This is my header file "funkcje.h"
#include <iostream>
using namespace std;
class Pracownik
{
private:
string nazwisko;
int pensja;
public:
Pracownik(string="",int=0);
~Pracownik();
string getNazwisko();
int getPensja();
friend double srednia_pensja(int,Pracownik);
};
class Informatyk : public Pracownik
{
private:
string certyfikat_Cisco;
string certyfikat_Microsoft;
public:
Informatyk(string="",int=0, string="", string="");
~Informatyk();
void info();
};
class Ksiegowy : public Pracownik
{
private:
bool audytor;
public:
Ksiegowy(string="",int=0, bool=false);
~Ksiegowy();
void info();
};
double srednia_pensja(int,Pracownik);
These are definitions of my functions "funkcje.cpp"
#include "funkcje.h"
Pracownik::Pracownik(string a,int b)
{
nazwisko=a;
pensja=b;
}
Pracownik::~Pracownik()
{
}
string Pracownik::getNazwisko()
{
return nazwisko;
}
int Pracownik::getPensja()
{
return pensja;
}
Informatyk::Informatyk(string a, int b, string c, string d) : Pracownik(a,b)
{
certyfikat_Cisco=c;
certyfikat_Microsoft=d;
}
Informatyk::~Informatyk()
{
}
Ksiegowy::Ksiegowy(string a, int b, bool c) : Pracownik(a,b)
{
audytor=c;
}
Ksiegowy::~Ksiegowy()
{
}
void Informatyk::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Certyfikat Cisco: "<<certyfikat_Cisco<<endl;
cout<<"Certyfikat Microsoft: "<<certyfikat_Microsoft<<endl;
}
void Ksiegowy::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Audytor: ";
if(audytor)
cout<<"Tak"<<endl;
else
cout<<"Nie"<<endl;
}
double srednia_pensja(int a,Pracownik *b)
{
return 0;
}
And finally main!
#include <iostream>
#include "funkcje.h"
using namespace std;
int main()
{
Pracownik lista[10];
Pracownik *lista_wsk = new Pracownik[10];
Informatyk a("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk b("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk c("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy d("Kowalski4",4000,1);
Ksiegowy e("Kowalski5",5000,0);
lista[0]=a;
lista[1]=b;
lista[2]=c;
lista[3]=d;
lista[4]=e;
Informatyk *ab = new Informatyk("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk *ac = new Informatyk("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk *ad = new Informatyk("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy *ae = new Ksiegowy("Kowalski4",3000,1);
Ksiegowy *af = new Ksiegowy("Kowalski5",3000,0);
lista_wsk[0]=*ab;
lista_wsk[1]=*ac;
lista_wsk[2]=*ad;
lista_wsk[3]=*ae;
lista_wsk[4]=*af;
for(int i;i<5;i++)
{
lista[i].info();
cout<<endl;
}
cout<<endl;
// for(int i;i<5;i++)
// {
// lista_wsk[i].info();
// }
return 0;
}
Ok and here goes my questions:
I had to create array which is filled with base class objects "Pracownik".
Secondary i had to create array which is full of pointers to class "Pracownik" objects.
(Hope those 2 first steps are done correctly)
Next thing I had to write to array 3 objects of class Informatic and 2 of class Accountant.
So I ve created 5 objects manually and added them into the array in such way array[0]=a;. I guess this is still good.
Next thing i had to create and add similar objects to array of pointers using new. So I ve created array with new and pointers to objects with new. (Hope thats correct 2).
And FINALLY:
I had to use info() on added to array objects.
This is my main question if my array is type "Pracownik" and I want to use function info() from subclasses how should I do that? And how compiler will know if he should use info() from Accountant or Informatic while I am trying to show those information using "for".
In an array of Pracownik, the elements are of type Pracownik. Any information about the objects being of a subclass of Pracownik are lost when you copy the elements into the array.
This is called object slicing and leads to the fact that there is no way to invoke Informatyk::info() on these objects.
If you want to call methods of a subclass, you have to prevent object slicing by storing pointers or references in the array.
As Oswald says in his answer,
Pracownik * lista_wsk = new Pracownik[10];
allocates an array of 10 Pracownik objects. This is probably not what you want. With polymorphism involved, we usually want to deal with pointers or references. Hence, you'd want an array of Pracownik * pointers. Since you already know at compile-time that it will have 10 members, there is no need for a dynamic allocation here. I think you've meant to write
Pracownik * lista_wsk[10];
instead. Now we don't put objects but pointers to objects into the array. For example:
lista_wsk[2] = new Informatyk("Kowalski3", 3000, "Cisco3", "Microsoft3");
And then we can iterate over the items like so:
for (unsigned i = 0; i < 10; ++i)
std::cout << lista_wsk[i]->getNazwisko() << std::endl;
As you have already discovered, it is impossible to call a subclass function member on a superclass object. It would be possible to figure out the actual type at run-time yourslf by means of a cast.
for (unsigned i = 0; i < 10; ++i)
if (Informatyk * info_ptr = dynamic_cast<Informatyk *>(lista_wsk[i]))
info_ptr->info();
dynamic_cast returns a pointer to the target class if this is possible or a nullptr (which evaluates to false, hence the conditional) otherwise. Note however that this is considered very poor style. It is better to use virtual functions. Therefore, add
virtual void
info()
{
// Do what is appropriate to do for a plain Pracownik.
// Maybe leave this function empty.
}
to the superclass and again to the subclass
virtual void
info() // override
{
// Do what is appropriate to do for an Informatyk.
}
The function in the subclass with the same signature is said to override the function inherited from the superclass. Since the function is marked as virtual, the compiler will generate additional code to figure out at run-time what version of the function to call.
If you are coding C++11, you can make the override explicit by placing the keyword override after its type as shown above (uncomment the override). I recommend you use this to avoid bugs that arise from accidental misspelling or other typos.
Related
I'm trying to writing some code for my c++ class. I'm using eclipse. I'm having a hard time trying to understand some of the instructions in the problem.
I've created a base class called Ship and then used inheritance for my CruiseShip class and CargoShip class.
For the CruiseShip class, I'm instructed to create
A print function that overrides the print function in the base class. The CruiseShip
class’s print function should display only the ship’s name and the maximum number
of passengers.
And similarly for the CargoShip class
A print function that overrides the print function in the base class. The CargoShip
class’s print function should display only the ship’s name and the ship’s cargo capacity.
I'm not sure what it means to "override" the print function in the base class.
It also instructs me to
Demonstrate the classes in a program that has an array of Ship pointers. The array
elements should be initialized with the addresses of dynamically allocated Ship ,
CruiseShip , and CargoShip objects. The program should then step through the array, calling
each object’s print function.
#include <iostream>
#include <string>
using namespace std;
class Ship
{
protected:
string ship_name;
int year_built;
public:
Ship()
{
ship_name="";
year_built=0;
}
void set_ship_name(string str)
{
ship_name=str;
}
void set_year(int y)
{
year_built=y;
}
int get_year()
{
return year_built;
}
string get_ship_name()
{
return ship_name;
}
void print(string, int)
{
cout<<"Ship name is "<<ship_name<<" and it was built in the year "<<year_built<<endl;
}
};
class CruiseShip: public Ship
{
private:
int max_passengers;
public:
CruiseShip()// :Ship(str,year)
{
max_passengers=0;
}
void set_passengers(int pass)
{
max_passengers=pass;
}
int get_passengers()
{
return max_passengers;
}
void print1(string, int)
{
cout<<"Ship name is "<<get_ship_name()<<" and max number of passengers are "<<max_passengers<<endl;
}
};
class CargoShip: public Ship
{
private:
int cargo_capacity_in_tons;
public:
CargoShip()//:Ship (str,year)
{
cargo_capacity_in_tons=0;
}
void set_capacity(int pass)
{
cargo_capacity_in_tons=pass;
}
int get_capacity()
{
return cargo_capacity_in_tons;
}
void print2(string, int)
{
cout<<"Ship name is "<<get_ship_name()<<" and its capacity is "<<cargo_capacity_in_tons<<" Tons."<<endl;
}
};
int main(){
CruiseShip ship1;
CargoShip ship2;
string ship_name1;
string ship_name2;
int year_built1;
int year_built2;
int max_passengers;
int cargo_capacity_in_tons;
cout<<"What is the name of the cruise ship?"<<endl;
cin>>ship_name1;
ship1.set_ship_name(ship_name1);
cout<<"What year was "<<ship_name1<<" built in?"<<endl;
cin>>year_built1;
ship1.set_year(year_built1);
cout<<"What is the maximum capacity of "<<ship_name1<<"?"<<endl;
cin>>max_passengers;
ship1.set_passengers(max_passengers);
//ship1.print(ship_name1, year_built1);
ship1.print1(ship_name1, max_passengers);
cout<<"What is the name of the cargo ship?"<<endl;
cin>>ship_name2;
ship2.set_ship_name(ship_name2);
cout<<"What year was "<<ship_name2<<" built in?"<<endl;
cin>>year_built2;
ship2.set_year(year_built2);
cout<<"What is the maximum capacity of "<<ship_name2<<" in tons?"<<endl;
cin>>cargo_capacity_in_tons;
ship2.set_capacity(cargo_capacity_in_tons);
ship2.print2(ship_name2, cargo_capacity_in_tons);
return 0;
}
Let´s say you have the following classes:
class Animal
{
private:
int x;
int y;
public:
virtual string sound() {return "Animal";}
void move() {x += 1; y+=1;}
};
class Cow
{
string sound() {return "Muh"} //this is overriding
string sound(string soundYouWant) {return soundYouWant;} //this is not overriding as string sound(string soundYouWant) is not the same as string sound()
void move() {x += 1; y+=1;} //this is also not overriding as move() in Animal has no virtual
};
So to summarize, overriding means you have a virtual method in the base class and you re-declare it in the derived class. This way, you are able to re-define it for every derived class (the method-body can be different for the base class and each of its derived classes).
Now to dynamic allocated arrays:
int size;
std::cin >> size;
int *array = new int[size]; //the array is stored on the heap
delete[] array; //deallocates the array and so frees the memory
If you create an array on the stack (without new), you either have to hardcode its size using literals (0, 1, 2, ...) or using a const int variableName. This way, the compiler knows the array size during compile time. So you have to know the array size while writing your program. Consequently, the compiler wouldn´t allow you to do this: std::cin >> size;.
Using new (dynamical arrays) you are allowed to specify the array size during compile time. So it is legal to let your program calculate the array size or take it as an user input. With dynamic arrays you also have a lot, lot, lot more memory than using the small stack (stackoverflow).
int *array: obviously the memory content is interpreted as integers. *array points to the first element of the array. int *array does NOT know the SIZE of the array. You have to keep track of that yourself.
new int[size]: You are reserving space for size * integers on the heap.
You might know that C++ does not have a garbage collector. This is when delete[] array; comes into play. When you don´t need array anymore (this includes other pointers pointing to array) you should call delete to free the memory. With small, short running programs, forgetting it won´t matter as the OS (operation system) will free the memory after your program has terminated. Nevertheless, you should use delete as not using it is very bad still and will lead to trouble with bigger programs. You should place delete in the destructor of a class (~clasname()) if you use array within a class.
I'm trying to make a chess program, but I want to be able to implement different AIs in it. Thus I made a abstract AIgeneric class and the derived class AIrandom off of AIgeneric. Then in my chessAI interface, I create a list of the the AIs, and try to call their getNextMove function and run into a segfault. The code is as below:
class AIgeneric {
public:
virtual int getNextMove(int*, const int &) = 0;
}
class AIrandom : public AIgeneric {
public:
AIrandom();
virtual int getNextMove(int*, const int &);
}
class chessAI {
public:
chessAI();
~chessAI();
void setAI();
int getNextMove(int*, const int &);
private:
vector<AIgeneric*> AIlist;
vector<string> names;
int selectedAI;
};
chessAI::chessAI () {
AIrandom randomAI;
AIlist.push_back(&randomAI);
names.push_back("Random AI");
selectedAI = -1;
}
int chessAI::getNextMove(int * board, const int & color) {
return AIlist[selectedAI]->getNextMove(board, color); //segfault on this line
}
It'd be great if anyone could help me on this problem!
Edit: I do set selectedAI to 0 before calling getNextMove.
In this code:
chessAI::chessAI () {
AIrandom randomAI;
AIlist.push_back(&randomAI);
names.push_back("Random AI");
selectedAI = -1;
}
You store a pointer to a local variable into your vector. After the constructor returns that pointer is no longer valid.
Remember that all local variables are stored on the stack, and the stack is reused in other functions. So when you use the pointer in the vector, it now points to some other functions memory and not the one object you declared.
This can be solved in three ways:
Allocate the object on the heap:
AIlist.push_back(new AIRandom);
Not using pointers at all.
Use smart pointers, such as std::unique_ptr.
You call selectedAI = -1; and then AIlist[selectedAI]->.... What do you expect AIlist[-1] to be, other than undefined behavior?
I expect this is because AIlist[selectedAI] is out of bounds. You can confirm this by replacing it with AIlist.at(selectedAI). Keep in mind that this index is -1 immediately after the constructor...
I have a problem I am working on. I have a number classes which inherit each other in this pattern:
#include <stdio.h>
#include <stdlib.h>
#include <list>
class TimeObject
{
public:
virtual void Tick()=0;
std::list<TimeObject*> ticks;
};
class MapObject : public TimeObject
{
public:
MapObject()
{
ticks.push_front(this);
printf("Create MapObject %p\n", this);
}
void Tick() { printf("mapobject tick\n"); }
};
class ControlObject : public MapObject
{
public:
ControlObject()
{
ticks.push_front(this);
printf("Create ControlObject %p\n", this);
}
void Tick() { printf("controlobject tick\n"); }
};
int main()
{
ControlObject test;
std::list<TimeObject*>::iterator it = test.ticks.begin();
for(; it != test.ticks.end(); it++)
{
TimeObject *trigger = *it;
trigger->Tick();
}
return 0;
}
The list in the example stores any TimeObject derived class. My problem is that when storing MapObject pointers in the list that are also ControlObjects dispatch always picks the ControlObject function.
Is it possible to trigger the MapObject function with a ControlObject pointer using polymorphism? If it isn't possible/pratical, what would be a good alternative?
You should always store pointer to the Base class A* in the list(std::list< A*>).
The pointer should be correctly made to point either a object of type Bor C before you add the pointer to the container.
Once you do that, dynamic dispatch will take care of calling the correct function for you depending on the actual object type. You don't need to do anything.
I don't know why you want to have any design which is otherwise, If you have any good reasons to do so please let know of them.
Why it always calls ControlObject::tick() in your code?
When you call:
ticks.push_front(this);
in ControlObject::ControlObject() you basically end up overwriting the first pointer you added to the list, The type of the first pushed pointer is not MapObject * anymore it is ControlObject * because you changed the pointer behind its back.You did not transfer ownership of the pointer to the list but you both had shared ownership and you modified the object in your list through the constructor call in derived class. This leaves you with two ControlObject * objects in the list which dynamic dispatch correctly determines and calls the correct method.
There is nothing wrong in what dynamic dispatch does, it is the correct behavior.
If you want to call MapObject::Tick(); then you will explicitly have to tell the compiler to do so, dynamic dispatch works on the actual type of object and it is working correctly.
void controlobject::Tick()
{
printf("controlobject tick\n");
MapObject::Tick();
}
Replicating from the comments:
I am afraid this is a bad design.The code works as it should,it works as defined by the C++ standard.The problem lies in the design.And unless you provide the details of what you are trying to achieve in a broader sense it is difficult and rather pointless to speculate on a new design.
Using a cast on the variable of type C to the type B should do the trick.
C c;
B b;
c.Function();
((B)c).Function();
A * l[] = {&c,&b,&c};
l[0]->Function();
l[1]->Function();
l[2]->Function();
B test = *(B*)l[0];
test.Function();
In your current example you should be able to call both virtual members (or just the one depending on the underlying type) by calling MapObject::Tick() inside ControlObject::Tick():
class ControlObject : public MapObject
{
public:
ControlObject()
{
ticks.push_front(this);
printf("Create ControlObject %p\n", this);
}
void Tick() { printf("controlobject tick\n"); MapObject::Tick(); }
};
The explicit function call notation is required.
Using C++ I built a Class that has many setter functions, as well as various functions that may be called in a row during runtime.
So I end up with code that looks like:
A* a = new A();
a->setA();
a->setB();
a->setC();
...
a->doA();
a->doB();
Not, that this is bad, but I don't like typing "a->" over and over again.
So I rewrote my class definitions to look like:
class A{
public:
A();
virtual ~A();
A* setA();
A* setB();
A* setC();
A* doA();
A* doB();
// other functions
private:
// vars
};
So then I could init my class like: (method 1)
A* a = new A();
a->setA()->setB()->setC();
...
a->doA()->doB();
(which I prefer as it is easier to write)
To give a more precise implementation of this you can see my SDL Sprite C++ Class I wrote at http://ken-soft.com/?p=234
Everything seems to work just fine. However, I would be interested in any feedback to this approach.
I have noticed One problem. If i init My class like: (method 2)
A a = A();
a.setA()->setB()->setC();
...
a.doA()->doB();
Then I have various memory issues and sometimes things don't work as they should (You can see this by changing how i init all Sprite objects in main.cpp of my Sprite Demo).
Is that normal? Or should the behavior be the same?
Edit the setters are primarily to make my life easier in initialization. My main question is way method 1 and method 2 behave different for me?
Edit: Here's an example getter and setter:
Sprite* Sprite::setSpeed(int i) {
speed = i;
return this;
}
int Sprite::getSpeed() {
return speed;
}
One note unrelated to your question, the statement A a = A(); probably isn't doing what you expect. In C++, objects aren't reference types that default to null, so this statement is almost never correct. You probably want just A a;
A a creates a new instance of A, but the = A() part invokes A's copy constructor with a temporary default constructed A. If you had done just A a; it would have just created a new instance of A using the default constructor.
If you don't explicitly implement your own copy constructor for a class, the compiler will create one for you. The compiler created copy constructor will just make a carbon copy of the other object's data; this means that if you have any pointers, it won't copy the data pointed to.
So, essentially, that line is creating a new instance of A, then constructing another temporary instance of A with the default constructor, then copying the temporary A to the new A, then destructing the temporary A. If the temporary A is acquiring resources in it's constructor and de-allocating them in it's destructor, you could run into issues where your object is trying to use data that has already been deallocated, which is undefined behavior.
Take this code for example:
struct A {
A() {
myData = new int;
std::cout << "Allocated int at " << myData << std::endl;
}
~A() {
delete myData;
std::cout << "Deallocated int at " << myData << std::endl;
}
int* myData;
};
A a = A();
cout << "a.myData points to " << a.myData << std::endl;
The output will look something like:
Allocated int at 0x9FB7128
Deallocated int at 0x9FB7128
a.myData points to 0x9FB7128
As you can see, a.myData is pointing to an address that has already been deallocated. If you attempt to use the data it points to, you could be accessing completely invalid data, or even the data of some other object that took it's place in memory. And then once your a goes out of scope, it will attempt to delete the data a second time, which will cause more problems.
What you have implemented there is called fluent interface. I have mostly encountered them in scripting languages, but there is no reason you can't use in C++.
If you really, really hate calling lots of set functions, one after the other, then you may enjoy the following code, For most people, this is way overkill for the 'problem' solved.
This code demonstrates how to create a set function that can accept set classes of any number in any order.
#include "stdafx.h"
#include <stdarg.h>
// Base class for all setter classes
class cSetterBase
{
public:
// the type of setter
int myType;
// a union capable of storing any kind of data that will be required
union data_t {
int i;
float f;
double d;
} myValue;
cSetterBase( int t ) : myType( t ) {}
};
// Base class for float valued setter functions
class cSetterFloatBase : public cSetterBase
{
public:
cSetterFloatBase( int t, float v ) :
cSetterBase( t )
{ myValue.f = v; }
};
// A couple of sample setter classes with float values
class cSetterA : public cSetterFloatBase
{
public:
cSetterA( float v ) :
cSetterFloatBase( 1, v )
{}
};
// A couple of sample setter classes with float values
class cSetterB : public cSetterFloatBase
{
public:
cSetterB( float v ) :
cSetterFloatBase( 2, v )
{}
};
// this is the class that actually does something useful
class cUseful
{
public:
// set attributes using any number of setter classes of any kind
void Set( int count, ... );
// the attributes to be set
float A, B;
};
// set attributes using any setter classes
void cUseful::Set( int count, ... )
{
va_list vl;
va_start( vl, count );
for( int kv=0; kv < count; kv++ ) {
cSetterBase s = va_arg( vl, cSetterBase );
cSetterBase * ps = &s;
switch( ps->myType ) {
case 1:
A = ((cSetterA*)ps)->myValue.f; break;
case 2:
B = ((cSetterB*)ps)->myValue.f; break;
}
}
va_end(vl);
}
int _tmain(int argc, _TCHAR* argv[])
{
cUseful U;
U.Set( 2, cSetterB( 47.5 ), cSetterA( 23 ) );
printf("A = %f B = %f\n",U.A, U.B );
return 0;
}
You may consider the ConstrOpt paradigm. I first heard about this when reading the XML-RPC C/C++ lib documentation here: http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#constropt
Basically the idea is similar to yours, but the "ConstrOpt" paradigm uses a subclass of the one you want to instantiate. This subclass is then instantiated on the stack with default options and then the relevant parameters are set with the "reference-chain" in the same way as you do.
The constructor of the real class then uses the constrOpt class as the only constructor parameter.
This is not the most efficient solution, but can help to get a clear and safe API design.
When I allocate a single object, this code works fine. When I try to add array syntax, it segfaults. Why is this? My goal here is to hide from the outside world the fact that class c is using b objects internally. I have posted the program to codepad for you to play with.
#include <iostream>
using namespace std;
// file 1
class a
{
public:
virtual void m() { }
virtual ~a() { }
};
// file 2
class b : public a
{
int x;
public:
void m() { cout << "b!\n"; }
};
// file 3
class c : public a
{
a *s;
public:
// PROBLEMATIC SECTION
c() { s = new b[10]; } // s = new b;
void m() { for(int i = 0; i < 10; i++) s[i].m(); } // s->m();
~c() { delete[] s; } // delete s;
// END PROBLEMATIC SECTION
};
// file 4
int main(void)
{
c o;
o.m();
return 0;
}
Creating an array of 10 b's with new and then assigning its address to an a* is just asking for trouble.
Do not treat arrays polymorphically.
For more information see ARR39-CPP. Do not treat arrays polymorphically, at section 06. Arrays and the STL (ARR) of the CERT C++ Secure Coding Standard.
One problem is that the expression s[i] uses pointer arithmetic to compute the address of the desired object. Since s is defined as pointer to a, the result is correct for an array of as and incorrect for an array of bs. The dynamic binding provided by inheritance only works for methods, nothing else (e.g., no virtual data members, no virtual sizeof). Thus when calling the method s[i].m() the this pointer gets set to what would be the ith a object in the array. But since in actuality the array is one of bs, it ends up (sometimes) pointing to somewhere in the middle of an object and you get a segfault (probably when the program tries to access the object's vtable). You might be able to rectify the problem by virtualizing and overloading operator[](). (I Didn't think it through to see if it will actually work, though.)
Another problem is the delete in the destructor, for similar reasons. You might be able to virtualize and overload it too. (Again, just a random idea that popped into my head. Might not work.)
Of course, casting (as suggested by others) will work too.
You have an array of type "b" not of type "a" and you are assigning it to a pointer of type a. Polymorphism doesn't transfer to dynamic arrays.
a* s
to a
b* s
and you will see this start working.
Only not-yet-bound pointers can be treated polymorphically. Think about it
a* s = new B(); // works
//a* is a holder for an address
a* s = new B[10]
//a* is a holder for an address
//at that address are a contiguos block of 10 B objects like so
// [B0][B2]...[B10] (memory layout)
when you iterate over the array using s, think about what is used
s[i]
//s[i] uses the ith B object from memory. Its of type B. It has no polymorphism.
// Thats why you use the . notation to call m() not the -> notation
before you converted to an array you just had
a* s = new B();
s->m();
s here is just an address, its not a static object like s[i]. Just the address s can still be dynamically bound. What is at s? Who knows? Something at an address s.
See Ari's great answer below for more information about why this also doesn't make sense in terms of how C style arrays are layed out.
Each instance of B contains Both X data member and the "vptr" (pointer to the virtual table).
Each instance of A contain only the "vptr"
Thus , sizeof(a) != sizeof(b).
Now when you do this thing : "S = new b[10]" you lay on the memory 10 instances of b in a raw , S (which has the type of a*) is getting the beginning that raw of data.
in C::m() method , you tell the compiler to iterate over an array of "a" (because s has the type of a*) , BUT , s is actualy pointing to an array of "b". So when you call s[i] what the compiler actualy do is "s + i * sizeof(a)" , the compiler jumps in units of "a" instead of units of "b" and since a and b doesn't have the same size , you get a lot of mambojumbo.
I have figured out a workaround based on your answers. It allows me to hide the implementation specifics using a layer of indirection. It also allows me to mix and match objects in my array. Thanks!
#include <iostream>
using namespace std;
// file 1
class a
{
public:
virtual void m() { }
virtual ~a() { }
};
// file 2
class b : public a
{
int x;
public:
void m() { cout << "b!\n"; }
};
// file 3
class c : public a
{
a **s;
public:
// PROBLEMATIC SECTION
c() { s = new a* [10]; for(int i = 0; i < 10; i++) s[i] = new b(); }
void m() { for(int i = 0; i < 10; i++) s[i]->m(); }
~c() { for(int i = 0; i < 10; i++) delete s[i]; delete[] s; }
// END PROBLEMATIC SECTION
};
// file 4
int main(void)
{
c o;
o.m();
return 0;
}