2

c++ newbie here

So I was checking out how classes work in c++, and I wrote this code:

class A{
   ...
}
class B: public A{
...
}
int main(void){
  A array[10];
}

Does inheritance in c++ work in the same way as inheritance in Java? Can I add objects of type B in an array and if so how would I do that? simply doing array[0] = new B(); does not work

P.S. Just a follow-up question, can someone please show me a quick example of an array of objects of a class that has a simple constructor? For some reason I had issues with that. If it's sort of complicated I'll just post another question. Thanks in advance

5
  • 1
    You may use A* array[10]; and then write array[0] = new B();. You can cast to base class only references and pointers of derived class. Simply doing array[0] = new B(); not working because it is impossible to assign A* to A. Commented Oct 19, 2014 at 21:54
  • 1
    Java uses pointers (called references). If you use pointers in C++ it works the same way (roughly). Commented Oct 19, 2014 at 21:59
  • @wowofbob Thank you, I have just tried that And I'm indeed able to store that in an array, even if I have an object C Commented Oct 19, 2014 at 22:01
  • Java orject references are not exactly like C++ pointers nor C++ references, IMHO it is best to learn each separately Commented Oct 19, 2014 at 23:26
  • The simplest and most flexible way is vector<unique_ptr<A>> Commented Oct 20, 2014 at 1:56

1 Answer 1

7

In Java, a variable of a class type is always a reference to the object. I.e.

A a = new A();
A a2 = a; // now a and a2 represent the same object
a.x = 12; // now print(a2.x) will also output 12

new A() will create the actual object on the heap (area of memory that can be dynamically allocated), and return the memory address of it. a and a2 will (internally) actually contain only that address.

Unlike integral type variables, which contain the actual value, so:

int a = 1;
int a2 = a;
a = 3;
// now a2 will still be 1

In C++ objects are put on the stack, the same way as integral types (unless you use references or pointers). So:

class A {
public:
   int x;
};

A a;
A a2;
a.x = 1;
a2 = a; // actually calls a2.operator=(a) which is implicitly defined, and assigns all data members (here only int x) the values from the other A
a.x = 2;
std::cout << a2.x << std::end; // will output 1

This is still true for arrays: If you declare an array A as[10], as is a series of 10 A objects allocated on the stack.

Inheritance

If you have

class A {
public:
    int x;
};
class B : public A {
public:
    int y;
};

A a;
a.x = 1;

B b;
b.x = 2;
b.y = 500;

// and then:
a = b; // now a.x == 2, but a holds no information about the y value

// with arrays it is the same:
A as[2];
as[0] = b; // now as[0].x == 2, --

Doing a = b copies only the values from b from its superclass, into a.

References

A a;
a.x = 1;
A& a_ref = a;

now a_ref is a reference to a unlike in Java the reference can't be made to point to another object. Doing a_ref = a2 would instead have the same effect as a = a2

a.x = 2
std::cout << a_ref.x << std::endl; // now outputs 2

Also unlike Java, a has to exist for as long as a_ref exist, otherwise a_ref is invalid and accessing it will make the program crash. In Java objects are allocated on the heap, and deallocated only when no reference exists that points to it anymore (this is called garbage collection).

B b;
A& a_ref2 = b;
// now a_ref2 is a reference to b.
// (aka B has been downcasted)
// to access b.y from a_ref2, you could do:
int the_y = static_cast<B&>(a_ref2).y

This (static upcasting), is not recommended and only works when you are sure that a_ref2 points to a B object. Otherwise it fill segfault/crash.

If A is polymorphic (see below), dynamic_cast<B&>(a_ref2) can be used instead (still not recommended). It detects the error if a_ref2 is not a B and throws an exception.

Polymorphism

class A {
public:
    virtual int get_v() { return 1; }
    int get() { return 1; }
    int get_a() { return 3; }
};
class B : public A {
public:
    int get_v() override { return 2; } // 'override' is not needed, and only works in C++11
    int get() { return 2; }
};

B b;
A& a_ref = b;

b.get_v(); // returns 2
b.get(); // returns 2
b.get_a(); // returns 3, by inheritance

a_ref.get_v(); // returns 2, because get_v is a virtual function.

Because of the virtual function A and B are polymorphic classes. It is not known at compile time whether this will call A::get_v or B::get_v, since a_ref is of type A&. Instead the function to call is decided at runtime, depending on what a_ref points to. In Java all functions are like this.

a_ref.get(); // returns 1.

Because get() is not polymorphic, it calls A's get() function since a_ref is of type A&.

a_ref.get_a(); // returns 3, by inheritance

Pointers

Pointers are like references, but lower level. You access references the same way as actual objects (a_ref.x and b.x). The pointer variable is the address. Unlike references, they can be made to point to another object after initial assignment:

B b; // same classes A and B as before
A* a_ptr = &b; // a is now a pointer to b.
// The type A* means "pointer to B". &b means "address of b".

// Polymorphism works the same:
a_ptr->get_v(); // returns 2
a_ptr->get(); // returns 1.
a_ptr->get_a(); // returns 3.

Dynamic memory allocation also returns pointers:

A* a_ptr = new B(); // a_ptr is now a pointer to a B allocated on the heap
...
delete a_ptr; // must be done to deallocate the B, otherwise there will be a memory leak.

Because a_ptr is of type A* (and not B*), the destructor of a polymorphic class should be virtual:

class A {
public:
    ...
    virtual ~A() { .... }
};
class B : public A {
public:
    ...
    ~B() override { .... }
};

otherwise only A::~A would get called.

So in your case, you could do:

A* array[10];
array[0] = new B();

array[0]->get_v();
delete array[0];

But all the pointers in the array that haven't been initialized would be invalid. I.e. array[1]->get_v() or delete array[1] would be an error.

array would then be an array of pointers to objects of type A, or a subclass of A.

You can also dynamically allocate an array of A's like this:

A* array = new[10] A; // array is now a pointer to an array
array[0].x = 1;
delete[] array; // Deallocates as many items as where allocated

But this is the same as doing A as[10] as before, just on the stack. These would be A objects , and doing array[0] = b would just copy the b.x from b.

unique_ptr

A good solution would probably to use std::unique_ptr from the STL. It is a wrapper for a pointer, such that no two unique_ptr can point to the same item. (because copying the unique_ptr is forbidden, etc.):

#include <memory>
// needed for std::unique_ptr

std::unique_ptr<A> array[10];
array[0].reset( new B() ); // needed to assign a value to it
array[0]->get_v();

works as expected. The elements of array that have not been assigned a value are default-initialized to zero. Accessing them would throw an exception instead of a segfault etc.

Items can't be assigned with = (because the semantics are different). Instead, reset assigns the pointer. If it had another pointer before, that one is delete'd first. Also no delete array[0] are needed (or possible), the unique_ptr deletes the items when the variable array goes out of scope.

A solution to allow multiple pointers pointing to the same object, such that the object gets deallocated only when no pointer points to it anymore, is also in the STL: shared_ptr.

Array constructors

For an array of items on the stack like

A array[10];

It will always call the default constructor (with no arguments). There is no way to pass arguments to it.

std::array can be copy-initialized (i.e. constructor taking single value:)

class A {
public:
    int x;
    A(int nx) : x(nx) {}
};

std::array<A, 3> ar = { 1, 2, 3 };
// or
std::array<A, 2> ar2 = { a, a2 }; // makes copies
Sign up to request clarification or add additional context in comments.

1 Comment

Solid answer, albeit a tad long

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.