c++: Behaviour of pointer data member in class inheritance hierarchy - c++

class Base {
protected:
string m_strName;
char* pchChar;
public:
Base()
{
m_strName = "Base";
pchChar = NULL;
};
void display()
{
printf(" display name : %s %c\n",m_strName.c_str(),pchChar);
};
};
class Derived : protected Base {
public:
Derived()
{
init();
};
void init()
{
m_strName = "Derived";
pchChar = (char*)malloc(sizeof(char));
strcpy(pchChar,"A");
printf(" char %c\n",*pchChar);
display();
};
};
int main()
{
Derived* pDerived = new Derived();
return 0;
}
The observed output is
char A
display name : Derived P
whereas i expected pchChar should have value "A" on both occasions.
am i missing any piece of information??
pls suggest.

You forgot *:
printf(" display name : %s %c\n",m_strName.c_str(), *pchChar);
It should be *pchChar, not pchChar. Because you're printing it as %c.
Or you can use %s as format string, and your printf would work, if the c-string is null-terminated string. Currently its not null-terminated. You should be doing this:
pchChar = (char*)malloc( 2 * sizeof(char)); //2 chars, one for `\0`
strcpy(pchChar,"A");
Or even better use new, and std::cout.
Also, don't forget to call free with malloc to deallocate the memory once you're done with it. And if you use new, use delete.

printf(" display name : %s %c\n",m_strName.c_str(),pchChar);
should be:
printf(" display name : %s %c\n",m_strName.c_str(),*pchChar);
The first will coerce the pointer into a character and will therefore depend entirely upon where that character is located in your address space.
The second will correctly dereference the pointer to get at the character, exactly as you have done so in the init code of your derived class.
And this little snippet:
pchChar = (char*)malloc(sizeof(char));
does not allocate enough space for the "A" string, you need two characters for that:
pchChar = (char*)malloc(2); // sizeof(char) is ALWAYS 1 !!!
Or better yet, don't use malloc at all. Although C++ provides those facilities for C compatibility, you're probably better off using new and delete.

Related

One warning and main() returned garbage value

Multiple students can associate with a single Department and single student can
associate with multiple Departments, but there is no ownership between the objects
and both have their own lifecycle. Both can create and delete independently.
WAP in C++ to model the relationships.
I have implemented this code as follows
#include<iostream>
#include<cstring>
using namespace std;
class Student
{
char* name_p;
public:
Student(char *sName)
{
cout<<"Student constructor called\n";
name_p=new char(sizeof(strlen(sName)));
name_p=sName;
}
~Student()
{
cout<<"Student destructor called\n";
delete name_p;
};
char* sName()
{
return name_p;
}
};
class Department
{
char* name_p;
public:
Department(char *dName)
{
cout<<"Department destructor called\n";
name_p=new char(sizeof(strlen(dName)));
name_p=dName;
}
~Department()
{
cout<<"Department destructor called\n";
delete name_p;
}
char* dName()
{
return name_p;
}
};
class Course
{
Student* std_p;
Department* dept_p;
char* courseName_p;
static unsigned int index;
static Course *courseList_p[4];
public:
Course(char* crseName,Student* student,Department* dept)
{
cout<<"Course constructor called\n";
std_p=student;
dept_p=dept;
if(index<4)
{
courseName_p=new char(sizeof(strlen(crseName)));
courseName_p=crseName;
courseList_p[index]=this;
++index;
}
else
{
cout<<"Cannot accomodate any more Course\n";
}
};
~Course()
{
cout<<"Course destructor called\n";
delete courseName_p;
};
static char* findStudent(char *crseName, char* deptName)
{
for(int i=0; i<index; i++)
{
if ( (courseList_p[i]->getCourseName() == crseName) &&
(courseList_p[i]->getDeptName() == deptName) )
{
return(courseList_p[i]->getStdName());
}
}
}
char* getStdName()
{
return std_p->sName();
};
char* getDeptName()
{
return dept_p->dName();
};
char* getCourseName()
{
return courseName_p;
};
};
unsigned int Course::index =0;
Course* Course::courseList_p[4]={0,0,0,0};
int main()
{
int i;
cout<<"\nExample of Association class\n";
cout<<"-----------------------------------\n\n";
cout<<"We have got 4 students\n";
Student *studentNames[4] = {new Student("Meera"), new Student("Moina"), new Student("Teena"), new Student("Mridula")} ;
cout<<"\n";
cout<<"We have got 2 Departments\n";
Department *departNames[2] = {new Department("Mathematics"), new Department("ComputerSceince")} ;
cout<<"\n";
cout<<"Here class Course Associates Student and Department, with a Course name\n";
Course course1("DataStructure",studentNames[0], departNames[1]);
Course course2("Maths",studentNames[3], departNames[0]);
Course course3("Geometry",studentNames[2], departNames[0]);
Course course4("CA",studentNames[1], departNames[1]);
cout<<"\n";
cout<<"Finding a Student using Course and Department\n";
cout<<"Student who has taken Maths Course in Mathematics Department is:"<<Course::findStudent("Maths", "Mathematics")<<endl;
cout<<"\n";
cout<<"Deletion of objects\n\n";
for(i=0;i<4;i++)
{
delete studentNames[i];
}
cout<<"\n";
for(i=0;i<2;i++)
{
delete departNames[i];
}
cout<<"\n";
return 0;
}
The code is showing warnings in main function i.e. ISO C++ forbids converting a string constant to char* and also the main() function is not returning 0 but a garbage value. Please help me in rectify these errors and warnings.
Also I don't want to use this pointer in class course, can I implement the code without using this pointer.
There are a couple of problems with your code. Mostly they are related to the use of char* instead of string. I'm amazed to see that some teacher still are found of this practice.
c-string allocation problems
The first problem causes memory corruption. This could explain that your main() returns garbage instead of 0. For example:
name_p=new char(sizeof(strlen(dName))); // OUCH!!!
In the statement you allocate it wrong. If you want to allocate an array, you must use new[...]. Moreover, keep in mind that a c-string allways needs 1 char more for the trailing null. So it should be:
name_p=new char[strlen(dName)+1];
would already improve the situation.
Last but not the least, if you allocate an array you must delete an array with delete[]
c-string copying issue
Then, you can copy C++ string with an assignment operator just as in:
name_p=sName; // OUCH
However, for char*, this just overwrites the pointer that new returned and you are not sure that the c-string it points to will still be valid when it's used.
If you want to assign a c-string, you need to go for:
strcpy (name_p, sName); // C-style
copy (sName, sName+strlen(sName)+2, name_p); // Safer C++ alternative
c-string comparison issue
Suppose you have two pointers to c string, say pa and pb. Comparing the pointers looks at the memory address, not at the content. So yu could have two identical strings but the comparison could nevertheless fail. THis kind of comparison would work perfectly well for c++ string, but for cstring, you need to to
strcmp(pa, pb)==0 // instead of pa == pb
c-strings in classes and structures
Whenever you have some pointers that you allocate in a class, you need to think of the rule of 3. Meaning that you'd need to add a copy constructor and an assignment operator. Fortunately, at first sight it seems that you don't need them here. Of couse, if you'd use c++ string you wouldn't have to worry about such details.
c-string conversion issues?
ANother issue in your code is the use of string literals with constructors. For example:
new Student("Meera").... // c-string literals (constant)
Student(char*...) // constructor using a c-string pointer (non constant)
You can easily get rid of this complaint, by making the constructor's pointer argument as constant:
Student(const char *sName)...
The same applies to function arguments, such as in find.
Other problems
The static member findStudent() works well, but only if a result is found. If nothing is found, it's UB, since there is no return statement (put a return nullptr; at the end of this function?)
Moreover, the wya you use this function, directly printing its result is dangerous, because dereferencing an invalid pointer will lead to UB.
Here the corrected code. It compiles and seem to run. I didn't analyse for more issues.

C++ derived Class constructor

I'm learning C++ inheritance and i have some problems with it.
I'm using Zinjal IDE with GNU c++ compiler.
This is the code:
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char str_init[])
{
strcpy(str, str_init);
}
};
class PString : String
{
public:
PString(char str_init[])
{
if (strlen(str_init) > MAX_STR_SIZE)
strncpy(str, str_init, MAX_STR_SIZE-1);
else
String(str_init);
}
};
Well it's just creating my own "string" class. But: What is the problem? The string can go too much bigger. So when the constructor of my "String" Class is called with a string the superssed "MAX_STR_SIZE" (which is defined as 80 chars for expamle) the program will crash with an array overflow ( >80 chars).
So i want to create a "child" or "derived" class called "PString" which can handles overflow.
As you can see PString child Class constructor checks the string if it's > MAX_STR_SIZE. If it's bigger than what char array can handle it cut off the string by MAX_STR_SIZE, so avoiding the overflow. If its smaller than MAX_STR_SIZE it calls the Parent class constructor.
But, g++ fails telling me "No matching function call to 'String::String()'.
It's a lame error i know, but i'm just learning
Thanks in advance.
You can't just call a constructor out of the blue. There are only specific places you can call a constructor and this isn't one of them.
The compiler sees String(str_init); and ssumes it is a function call but you don't have a matching function - hence the error.
As per the comment below there is a subtlety here. The error message is "No matching function call to 'String::String()". In a class called String a method called String is going to be a constructor. Therefore the line String(str_init); isn't a function call, it is trying to make a String item using a non-existent default constructor.
what you are trying to do does not require this set of complications. In general strncpy will handle both cases correctly. However, for the education sake here is some analysis
1.In your constructor of the PString you call the construct of the String and ask it to strcpy str_intit into str. So, you cause the same memory corruption you wanted to avoid. you are missing String() constructor. I suggest to create the default constructor for the String and remove the one you use from PString.
String() {str[0] = 0;}
..
PString(char str_init[])
{
2.your case could be modified a bit as the following:
if (strlen(str_init) > MAX_STR_SIZE) {
strncpy(str, str_init, MAX_STR_SIZE-1);
str[MAXLEN-1] = 0; // strncpy will not add '0' in this case. you need to do it yourself
}
else
strcpy(str, str_init);
above, in the if clause you use strncpy whether after the 'else' you can use the simple strcpy. So, it looks like this:
#include <cstring>
#define MAX_STR_SIZE 80
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char *str_init)
{
strcpy(str, str_init);
}
String() {
str[0] = 0;
}
};
class PString : String
{
public:
PString(char str_init[])
{
if (strlen(str_init) > MAX_STR_SIZE) {
strncpy(str, str_init, MAX_STR_SIZE-1);
str[MAX_STR_SIZE-1] = 0;
}
else
strcpy(str, str_init);
}
};
2 Errors.
First, you need to invoke the base constructor via your class' initializer list as explained in this answer.
Second you need to assign some object to your class when calling
String(str_init);
The following code compiles on my machine.
#include <cstring>
#define MAX_STR_SIZE 20
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char str_init[])
{
std::strcpy(str, str_init);
}
};
class PString : String
{
public:
PString(char str_init[]) : String(str_init)
{
if (strlen(str_init) > MAX_STR_SIZE)
strncpy(str, str_init, MAX_STR_SIZE-1);
else
String s = String(str_init);
String s2(str_init);
int i = 0;
i++;
}
};

C++ Copy constructor and Operator

I was trying to understand Copy constructor and Operator. I read some code but I just didn't get it.
Here is main function
int main()
{
Cwin win1('A',"window")
Cwin win2;
win1 = win2;
win1.set_data('B',"hello");
win1.show();
win2.show();
}
I extract the most important code from Class Cwin
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
......
};
Output :
B : hello
B : hello
I can understand what causes it. But I can't understand below solution that fix it.
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
void operator=(const Cwin &win)
{
id = win.id;
strcpy (this->title , win.title);
}
......
};
Output:
B : hello
D : default
Why change it strcpy (title , win.title); into strcpy (this->title , win.title); makes a huge difference?
Explanation:
Inside of a member function, you can write this->title or title. Both are equivalent, unless you have a local function variable called title which name would then hide the member.
WHat makes the difference is that you have added an assignement operator.
Without assignement operator your the statement win1=win2 proceeds by copying member by member. In this situation, a pointer is copied as is, so that after the copy, both objects will point to the same pointed char*:
When you then set_data(), the string is copied to the char* pointer by win1, but win2 points to the same value.
Your second code snippet, handles the copy much better: the content of the string pointed to is copied/duplicated to char* pointed by the other object. win1 and win2 will hence continue to use 2 distinct strings.
Remarks/advices
You use strcpy() without checking that the target is long enough to contain the copied string. This might result in buffer overflow, memory corruption, and a lot of other horrible things.
I'd strongly advise that you'll use std::string instead of char* whenever possible: The default copy would have been well managed, and you don't have to care for memory allocation/deallocation,length.
C++ classes have a default assignment operator, that does a shallow copy whenever you assign one member of that class to other member unless you overload it. (except at the time of initialization, where it uses the copy constructor )
Now, coming to your code in the first part there is no overloading of the assignment operator, therefore what it does is do the shallow copy of the elements of win2 to win1 which in turn copies the 'id' and the 'title' pointer(not the string what it is pointing to(storing)).
The main function will run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will do somewhat the following
win1.id = win2.id;
win1.title = win2.title; ( note that the pointer is copied )
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
Also since win1.title and win2.title are both the same pointer
so win2.title will also point to "hello"
*/
win1.show();
win2.show();
}
But in the second part you have overloaded the assignment operator which instead of copying the pointer copies the string it is pointing to(storing).The main will now run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will now do what is in the overloaded assignment operator
where to copy the string strcpy is used which will copy the content
of the string instead of copying the pointers
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
win2.title will still point to "Default"
*/
win1.show();
win2.show();
}
Hence , the given results.
And you must also see and follow the advice given in this answer.
If Cwin does not explicitly implement its own assignment-operator (which your first example does not), the compiler will generate a default implementation that simply copies member values as-is from one object to another. That is why your output said the same thing for both objects - the win1 = win2 statement using the default assigment-operator implementation merely overwrote the pointer value of win1.title with the pointer value of win2.title, and thus both title members were left pointing at the same memory block, which the subsequent win1.set_data() statement populated with data.
To do what you are asking, you should change title to use std::string instead of char*, let it handle the copying for you. It will work fine with the compiler's default copy-constructor and assignment-operator implementations (unless you have other data that you need to make manual copies of):
#include <string>
Class Cwin
{
private:
char id;
std::string title;
public:
Cwin()
: id('D'), title("default") {}
Cwin(char i, const std::string &text)
: id(i), title(text) {}
void set_data(char i, const std::string &text)
{
id = i;
title = text;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
But, if you need to use char* directly, you must implement the copy-constructor and assignment-operator properly to ensure your title data gets copied correctly, eg:
#include <cstring>
Class Cwin
{
private:
char id, *title ;
public:
Cwin()
: id(0), title(NULL)
{
set_data('D', "default");
}
Cwin(char i, char *text)
: id(0), title(NULL)
{
set_data(i, text);
}
Cwin(const Cwin &win)
: id(0), title(NULL)
{
set_data(win.id, win.title);
}
~Cwin()
{
delete[] title;
}
Cwin& operator=(const Cwin &win)
{
if (this != &win)
set_data(win.id, win.title);
return *this;
}
void set_data(char i, char *text)
{
int len = std::strlen(text);
char *newtitle = new char[len+1];
std::strcpy(newtitle, text);
delete[] title;
title = newtitle;
id = i;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
Any time you have to manually allocate memory in a constructor and free it in a destructor, chances are good that you will also need to copy that data in a copy-constructor and assignment-operator as well. Read up about the The rule of three/five/zero. In this situation, CWin needed to follow the Rule of three in order to function properly with char*, but can follow the Rule of zero when using std::string. You should always strive to write code that follows the Rule of zero whenever possible, it makes things easier to manage.

C++ classes , Object oriented programming

I have a very simple class named person which is given below , I have a problem with only two functions , i.e setstring () function and setname() function , I am calling setstring() function from the setname function.
The only problem is when in the main function I write
Object.setname(“Zia”);
The result is ok as shown in the output screen,
Now when I write
Object.setname(“Zia ur Rahman”);
Nothing is displayed as you can see the output screen.
I know the problem is when I pass the name pointer to setstring () function but I am confused about it please explain it in detail that what is happening here.
#include<iostream.h>
class person
{
char* name;
public:
person();
void setname(const char*);
void setstring(const char*, char*);
void print()const;
};
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0';
}
}
void person::setname(const char* n)
{
setstring(n, name);//passing data member name
}
void person::print()const
{
cout<<"Name: "<<name<<endl;
}
main()
{
person object;
object.setname("Zia ur Rahman");
object.print();
system("pause");
}
alt text http://img264.imageshack.us/img264/8867/inheritanceimage004.jpg
alt text http://img263.imageshack.us/img263/3770/inheritanceimage003.jpg
The specific reason that nothing is being printed is that in setstring, p is copy of the name pointer, not a reference to it. Try changing the signature of setstring to:
void setstring(const char* s, char*& p);
(note the &).
See the other answers for other significant errors in the code - unless these problems are fixed, you are likely to get crashes or strange behaviour.
And unless the purpose of the code is just to learn dynamic arrays, use std::string instead :-).
If you want to do correct OO-oriented programming in C++ you should maybe stay away from direct pointer management but use the STL string class and use references instead of pointers. Then you should have an easier time and your source should produce the correct output.
Otherwise check your constructor of the person class, the name-array has just 3 elements but you're referencing the 4th one there (indices 0-2!). Also in the setstring() method you're not allocating enough space for the trailing '\0' in the array!
For starters:
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
name has three elements - you are treating it as if it had five. In a similar vein:
p=new char[strlen(s)];
should be:
p=new char[strlen(s) + 1];
In fact, that function is completely wrong - you are supposed to be copying the string into 'name', not the mysterious 'p' parameter. If you really want that function, then 'p' must be a pointer to a pointer or a reference to a pointer.
Have you thought about using std::string from STL?
Though first problem I see is this.
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
Your allocating an array of char's, the size of the array is 3 characters, then your copying "NILL" into it with strcpy so your filling in the array with all the characters but without the null terminator \0. "NILL" is a const string and such has a null terminator that is implicit but not shown for example "NILL\0". The \0 is a control character that is used to indicate the end of the string. Then you have an index out of bounds when you access the 3rd element of name array,when your size of your array is 3.
To help you find out the other parts that could be going wrong, here are some of the links to the function you use.
strcpy
strlen
strcpy will copy the whole string from one buffer to the next including the null terminator control character.
Strlen will return amount of characters between the beginning of the string and the terminating null character. Though it will not count the null character as a character. Thats why you will see suggestions with strlen(string)+1 to include the null terminator.
Though your doing well keep up the work, you will understand these little gotcha when using the standard library functions.
Suppose s have 5 chars. Then new char[strlen(s)]; allocates the memory of 5 chars. Then p[strlen(s)]='\0' is equivalent to p[5]=0. It's very very bad. p[5] does not exsist.
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0'; //<<<< LOOK! Out of range
}
}
It's rather C, than C++ code.
There is C++ equivalent:
#include <string>
#include <iostream>
class person
{
std::string name_;
public:
person();
//preferable design is to set object invariant in appropriate constructor
explicit person(const std::string &name);
std::string get_name() const;
void set_name(const std::string &name);
void print()const;
};
person::person()
: name_("NILL")
{}
person::person(const std::string &name)
: name_(name)
{}
std::string person::get_name() const
{
return name_;
}
void person::set_name(const std::string &name)
{
name_ = name;
}
void person::print()const
{
std::cout<<"Name: "<<name_<<std::endl;
}
int main()
{
person person1;
person1.set_name("Zia ur Rahman");
person1.print();
//this is better design decision
//because after constructor we have valid object
person person2("Zia ur Rahman");
person2.print();
std::cin.get();
return 0;
}

C++ new[] into base class pointer crash on array access

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;
}