Can a Pointer to Base Point to an Array of Derived Objects

Base pointer to array of derived objects

If you look at the expression p[1], p is a Base* (Base is a completely-defined type) and 1 is an int, so according to ISO/IEC 14882:2003 5.2.1 [expr.sub] this expression is valid and identical to *((p)+(1)).

From 5.7 [expr.add] / 5, when an integer is added to a pointer, the result is only well defined when the pointer points to an element of an array object and the result of the pointer arithmetic also points the an element of that array object or one past the end of the array. p, however, does not point to an element of an array object, it points at the base class sub-object of a Derived object. It is the Derived object that is an array member, not the Base sub-object.

Note that under 5.7 / 4, for the purposes of the addition operator, the Base sub-object can be treated as an array of size one, so technically you can form the address p + 1, but as a "one past the last element" pointer, it doesn't point at a Base object and attempting to read from or write to it will cause undefined behavior.

Can a pointer to base point to an array of derived objects?

You cannot index like that. You have allocated an array of Rectangles and stored a pointer to the first in shapes. When you do shapes[1] you're dereferencing (shapes + 1). This will not give you a pointer to the next Rectangle, but a pointer to what would be the next Shape in a presumed array of Shape. Of course, this is undefined behaviour. In your case, you're being lucky and getting a crash.

Using a pointer to Rectangle makes the indexing work correctly.

int main()
{
Rectangle * shapes = new Rectangle[10];
for (int i = 0; i < 10; ++i) shapes[i].draw();
}

If you want to have different kinds of Shapes in the array and use them polymorphically you need an array of pointers to Shape.

initialize a base class pointer with an array of derived class objects

This will not work - C arrays do not know about the dynamic size of polymorphic types. If you want to use polymorphism, then you have to use arrays (preferably std::vector or another standard array, not a C array) of pointers (preferably smart pointers) to the base type.

Pointer to array of base class, populate with derived class

Your array is of the wrong type: it stores BaseClass object instances instead of pointers to them. Since BaseClass seems to be abstract, the compiler complains that it cannot default-construct instances to fill your array.

Even if BaseClass were not abstract, using arrays polymorphically is a big no-no in C++ so you should do things differently in any case.

Fix this by changing the code to:

BaseClass** base = new BaseClass*[2];

base[0] = new FirstDerivedClass;
base[1] = new SecondDerivedClass;

That said, most of the time it is preferable to use std::vector instead of plain arrays and smart pointers (such as std::shared_ptr) instead of dumb pointers. Using these tools instead of manually writing code will take care of a host of issues transparently at an extremely small runtime cost.

Assigning derived class array to base class pointer

You have been unlucky enough to hit one of the sweet spots of the type system that allows to compile perfectly invalid code.

The function int sum1 (B* arr) takes a pointer to a B object as argument according to the signature, but semantically it really takes a pointer to an array of B objects. When you call sum1(arrD) you are violating that contract by passing not an array of B objects, but rather an array of D objects. How do they differ? Pointer arithmetic is done based on the size of the type of the pointer, and a B object and a D object have different sizes.

An array of D is not an array of B

In general, a container of a derived type is not a container of the base type. If you think about it, the contract of a container of D is that it holds, well, D objects, but if a container of D was a container of B, then you would be able to add B objects (if the argument was extending, you might even consider adding D1 objects --also derived from B!).

If instead of raw arrays you were using higher order constructs, like std::vector the compiler would have blocked you from passing a std::vector<D> in place of a std::vector<B>, but why did it not stop you in the case of an array?

If an array of D is not an array of B, why did the program compile at all?

The answer to this predates C++. In C, all arguments to functions are passed by value. Some people consider that you can also pass-by-pointer, but that is just passing a pointer by-value. But arrays are large, and it would be very expensive to pass arrays by value. At the same time, when you dynamically allocate memory you use pointers, although conceptually, when you malloc 10 ints you are allocating an array of int. The designers of the C language considered this and made an exception to the pass by value rules: if you try to pass an array by value, a pointer to the first element is obtained, and that pointer is passed instead of the array (a similar rule exists for functions, you cannot copy a function, so passing a function implicitly obtains a pointer to the function and passes that instead). The same rules have been in C++ since the beginning.

Now, the next problem is that the type system does not differentiate from a pointer to an element when that is all there is, and a pointer to an element that is part of an array. And this has consequences. A pointer to a D object can be implicitly converted to a pointer to B, since B is a base of D, and the whole object of OO programming is being able to use derived types in place of base objects (well, that for the purpose of polymorphism).

Now going back to your original code, when you write sum1( arrD ), arrD is used as an rvalue, and that means that the array decays to a pointer to the first element, so it effectively is translated to sum1( &arrD[0] ). The subexpression &arrD[0] is a pointer, and a pointer is just a pointer... sum1 takes a pointer to a B, and a pointer to D is implicitly convertible to a pointer to B, so the compiler gladly does that conversion for you: sum1( static_cast<B*>(&arrD[0]) ). If the function just took the pointer and used it as a single element, that would be fine, as you can pass a D in place of a B, but an array of D is not an array of B... even if the compiler allowed you to pass it as such.

Vector of pointers to base class containing base and derived class objects - accessing derived-class specific variables

Yes it is possible. You only need to cast the pointer. The simplest syntax is:

((Derived*)e)->DerviedVariable

which is equivalent (modulo casting away constness, if any) to C++ish

static_cast<Derived*>(e)->DerviedVariable

The word “static” here reminds that there is no runtime checking: the compiler trusts you that e indeed points to an instance of Derived. If it doesn’t, undefined behavior occurs. The safer alternative is dynamic_cast:

Derived *ee = dynamic_cast<Derived*>(e);
if (ee)
x = ee->DerviedVariable;

It returns NULL if the object is not an instance of Derived. (Note that references can be casted as well, but as there is no NULL reference, dynamic_cast will throw instead if cast is not possible)

Nevertheless, using such casts is often considered a bad practice, for a reason. Virtual functions are preferable, mostly because using them don’t require you to even know the actual object type at the call point.

Array of Pointers to Different Derived Classes

you are maintaining an array of channel obejcts.

For virtual function to work you need to maintain either pointer of base class type or a reference.

Channel *Channels;

creates an array of Channel obejcts. You need to create an array of pointer/reference to channel object i.e.

Channel **Channels;

Change the program accordingly to use pointer instead of object of channel type.

Access member of derived class from pointer of base class

I cannot access members belonging to the derived class with pointer notation

This is by design: you did not tell the compiler that the object pointed to by the pointer is of the derived type.

is it possible for me to access these members with a simple command

You can do it if you perform static_cast<B*>(arr[0]) if you are 100% certain that the pointer points to B, but casting solution should be used as the last resort. Instead, you should derive a member function in the base class, and provide an implementation in the derived class:

class A {
public:
int foo;
virtual char get_bar() = 0;
};

class B : public A {
char bar;
public:
char get_bar() {
return bar;
}
};


Related Topics



Leave a reply



Submit