0

It is my understanding that we can never instantiate an abstract class in C++ or equally an interface in Java. This makes sense because we have "pure virtual" functions that provide no implementation, therefore we can only instantiate subclasses of this class/interface that adhere to the contract formed by the abstract class. Consider the following code:

#include <iostream>
#include <vector>

using namespace std;

class AbstractClass {
public:
  AbstractClass() {
    cout << "Instantiating" << endl;
  }

  virtual void pureVirtualFunction() = 0;

  void testMe() {
    cout << "test" << endl;
  }
};

int main() {
  vector<AbstractClass*> v;
  v.resize(100);
  cout << v.size() << endl;
  v[0]->testMe();
  v[0]->pureVirtualFunction(); //segfault on my machine
  return 0;
}

What baffles me when I run this on my computer is that I can actually resize the vector. I always thought that std::vector::resize instantiated some number of elements, thus calling each element's constructor but further research shows that the constructor is not actually called (also evident by my stdout not showing 100x "Instantiating" strings).

So if std::vector::resize allocates space for these new objects, and allows me to actually call methods on each of them, how are they not fully instantiated? I figured I couldn't even call resize() on a vector of abstract classes but I can, I assume because they are not fully initialized. Could someone explain what is happening here?

EDIT:

Brain fart..forgot that a vector of pointers, when resized, does not actually allocate new elements, as pointers can be created without allocated elements behind them, however...why can I still call a member function on a pointer to a class in which is not instantiated?

Dominic Farolino
  • 1,362
  • 1
  • 20
  • 40
  • You are referring to a pointer in the vector. There won't be any objects crated of the abstract class. – πάντα ῥεῖ Sep 18 '16 at 19:16
  • 2
    The answers below aside, it is kind of interesting that calling testMe may "work". I believe a similar explanation to what is given [here](http://stackoverflow.com/questions/2505328/calling-class-method-through-null-class-pointer) may apply. – Robert Prévost Sep 18 '16 at 19:25
  • 1
    If you want to increase the likelihood that the madness of `v[0]->testMe()` will actually parade its *undefined behavior*, declare a member variable `int foo;` in `AbstractClass`, and use it in some fashion (assign it as `foo = 42;`, for example) in `testMe()`, in addition to your stdout-write. – WhozCraig Sep 18 '16 at 19:31
  • And with your edit-update, the link provided by Robert ([repeated here](http://stackoverflow.com/questions/2505328/calling-class-method-through-null-class-pointer)) becomes even more relevant. – WhozCraig Sep 18 '16 at 19:37
  • @WhozCraig Thanks! – Dominic Farolino Sep 18 '16 at 19:46

3 Answers3

2

The other answers already say what's wrong with your code, I'm here to answer the edit:

however...why can I still call a member function on a pointer to a class in which is not instantiated?

You mean this part in particular:

  v[0]->testMe();
  v[0]->pureVirtualFunction(); //segfault on my machine

Basically, because v[0] doesn't point to a valid object yet trying to call testMe() is already undefined behaviour.

The only reason it doesn't segfault is because you're (un)lucky that the compiler rewrote your code. Your body of the method testMe() doesn't require the *this object at all and thus the compiler will just replace it with a cout call. Because no attempt to access some invalid this pointer is made there is no segfault yet. Then, when calling the second method the compiler needs to check the vtable of an invalid this pointer, which causes the segfault. Don't ever rely on the behaviour of the testMe() working out, this is still undefined behaviour, anything can happen.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Thank you so much. Yes I found out that whenever `testMe()` used `this` to say, set a member variable we'd then segfault (which totally makes sense because there technically is no `this`) – Dominic Farolino Sep 18 '16 at 19:45
2

The value_type of your vector is AbstractClass* - that is, the vector contains pointers which could be nullptr, a pointer to a valid instance of AbstractClass deriviation or random locations in memory.

vector::resize(N) creates N default-initialized elements, and again, the value_type of your vector is something*, so it will create N instances of nullptr.

AbstractClass::testMe is a non-virtual member function. Thus, under the hood, it is simply a plain function that takes a hidden this pointer as its first argument.

The body of testMe itself does not access any elements of the abstract class, so it never dereferences this.

Thus

v[0]->testMe();

calls AbstractClass::testMe(nullptr, ); and never dereferences this.

v[0]->pureVirtualFunction(); //segfault on my machine

tries to dereference nullptr to find v[0]s vtable => crash.

kfsone
  • 23,617
  • 2
  • 42
  • 74
0

You made a vector of AbstractClass *, not of AbstractClass.

Pointers can be created without objects to point to.

Because of the function being virtual, those pointers could point to derived classes' objects which are no longer abstract, so the call would be legal.
Only when you finally execute it, the system trips over the null in the vptr table and dumps.

Aganju
  • 6,295
  • 1
  • 12
  • 23