-9

It was an interview question that we write Shape s = new Circle(); where Shape is an abstract class.

What is difference between s and a pointer of shape type like Shape * s = new Circle();?

2 Answers2

2

These two statements are quite distinct from one another, I'm going with the easy (and correct) one first:

The pointer

Shape * s = new Circle();

As you already mentioned, s is a pointer-to-Shape in this case. The statement declares s, and assigns a pointer of type Circle to it. This implies that Circle is derived from Shape.

Because C++ is a polymorphic language, s does now point to an object of type Circle. If Shape has virtual functions that are overloaded by Circle, calls to those functions on s will be redirected to the overloading Circle implementation.

A simple and incomplete example (note that in C++, there are no by-keyword defined abstract classes):

class Shape{
    virtual int getX() = 0; //getX() is purely virtual, so Shape is abstract   
}
class Circle : public Shape{
    int getX() override;
}

Shape * s = new Circle();

int myX = s->getX(); //will call Circle::getX()

This is basic polymorphic behaviour, and one of the foundation stones of Object Oriented Programming in C++. Also note that the override keyword is not entirely necessary, I just personally consider it to be good style.

The Object

Shape s = new Circle();

This statement looks nearly like the first one, but does something completely different. First of all, s is now the actual object, which is good and bad at the same time. It's good because it will get automatically destroyed as soon as the current compound statement is left, freeing all memory occupied by it.

But it's bad because s can not be polymorphic anymore: It is declared as a Shape-Object, and nothing else. With the code from above, and your premise that Shape is an abstract class, that wouldn't even work. Abstract classes cannot be instantiated, because they have unimplemented functions.

If we assumed that it wasn't abstract, we would fall into a lot of implicit conversion loopholes, which I only want to describe in a short fashion:

In order for the above statement to be correct C++ code, Shape would need to have a constructor that takes one argument of type Circle*, that is not declared as explicit. This would make Shape s = new Circle(); equivalent to Shape s = Shape(new Circle());. An implicit conversion takes place.

This is very, very bad code! new Circle() allocates memory for a Circle object that can never be freed, because it has no name! This is called a memory leak, and it's the reason why you should almost never use naked pointers in C++ any more. The alternative is called smart pointers, and you should use them whenever possible.

Additionally, to prevent things like this from happening, you should declare all your constructors explicit (or at least the ones that take only one argument):

explicit Shape(Circle *);

Finally, I would like to encourage you to read at least one, better a few books about C++. Everything I just explained is super basic stuff that you should really know, and this is not the best place to learn it. C++ is a dangerous language, you can do a lot of things very wrong, even if they seem to work initially.

Community
  • 1
  • 1
iFreilicht
  • 13,271
  • 9
  • 43
  • 74
  • Good answer, I'd also suggest to the original poster that if these concepts are confusing then its probably not the right time to be interviewing for C++ jobs! – Matt Coubrough Jun 12 '14 at 23:44
1

new Circle creates a dynamic object of type Circle and gives a pointer to it. That pointer can be used to initialise a pointer to Circle or any base class of Circle; so assuming that Circle derives from Shape, the second form

Shape * s = new Circle;

gives a pointer of (static) type Shape*, pointing to an object of (dynamic) type Circle. This can be used polymorphically, to interact with the Circle via the abstract interface defined by Shape, without needing to know that the actual type is Circle.

The first, incorrect, example

Shape s = new Circle;

attempts to create an object of type Shape, initialised with the pointer to Circle. Being abstract, Shape can't be instantiated except as part of a derived class, so this fails to compile.

When you use new, always remember to delete the dynamic objects when you've finished with them or, better still, use RAII classes such as smart pointers to handle that error-prone tedium for you. Also, make sure that Shape has a virtual destructor so that its derived types can be safely deleted.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644