views:

6927

answers:

9

I'm trying to use an abstract class when passing an extended object as an parameter to a function, but my attempts so far have led to some compiler errors.

I have a few clues as to what the problem is, I'm obviously not allowed to instantiate an abstract class, and I believe some of the code in MyClass is trying to do this, even though this is not my intention. Some researching has suggested that I should reference the object as a pointer to achieve what I want, but my attempts so far have failed and I'm not even sure that this is the answer (hence my asking here).

I'll submit now that I'm more familiar with Java than C++, and I'm sure part of my problem is due to this.

Here is an example of what I'm trying to do in my program:

class A {
    public:
     virtual void action() = 0;
};

class B : public A {
    public:
     B() {}

     void action() {
      // Do stuff
     }
};

class MyClass {

    public:

     void setInstance(A newInstance) {
      instance = newInstance;
     }

     void doSomething() {
      instance.action();
     }

    private:

     A instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(myInstance);
    c.doSomething();
    return 0;
}

This example produces the same compiler error that I am getting in my program:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
test.cpp:20: error: cannot declare parameter ‘newInstance’ to be of abstract type ‘A’
test.cpp:2: note:   because the following virtual functions are pure within ‘A’:
test.cpp:4: note:   virtual void A::action()
test.cpp:30: error: cannot declare field ‘MyClass::instance’ to be of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions
test.cpp: In function ‘int main(int, char**)’:
test.cpp:36: error: cannot allocate an object of abstract type ‘A’
test.cpp:2: note:   since type ‘A’ has pure virtual functions

Update

Thanks for the feedback everyone.

I've since changed "MyClass::instance to contain a pointer of type A, but I now get some bizarre errors related to the vtable:

sean@SEAN-PC:~/Desktop$ gcc -o test test.cpp
/tmp/ccoEdRxq.o:(.rodata._ZTI1B[typeinfo for B]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTI1A[typeinfo for A]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
/tmp/ccoEdRxq.o:(.rodata._ZTV1A[vtable for A]+0x8): undefined reference to `__cxa_pure_virtual'
collect2: ld returned 1 exit status

My modified code is as follows (A and B have not been modified):

class MyClass {

    public:

     void setInstance(A* newInstance) {
      instance = newInstance;
     }

     void doSomething() {
      instance->action();
     }

    private:

     A* instance;
};

int main(int argc, char** argv) {
    MyClass c;
    B myInstance;
    c.setInstance(&myInstance);
    c.doSomething();
    return 0;
}
+5  A: 

What you are doing would work in Java because declaring a parameter or member variable of type "A" really means a "pointer to an A". In C++, you actually need to be explicit about that since they are two different things:

void setInstance(A* newInstance) { // pointer to an "A"
                instance = newInstance;
}

And in the declaration:

A* instance; // Not an actual "A", but a pointer to an "A"
Eric Petroelje
don't mix up pointers and references.
n0rd
What you explained makes sense, but since I've declared "instance" as a pointer I get linker errors related to the vtable... I will update my question to expand on this.
seanhodges
@n0rd - My bad, I was going to use references then switched to pointers after noticing that his declaration wouldn't work as a reference (refs can't be null). I'll edit and fix it.
Eric Petroelje
+1  A: 

You should store A as a pointer.

A* instance;

Edit: I've written "reference" before. There is a difference in C++.

stepancheg
Isn't that a pointer???
Dominic Rodger
that is not a reference, it is a pointer. Please edit your answer.
Naveen
A: 

You must use a pointer to A as a member of MyClass.

class MyClass {

    public:

        void setInstance(A *newInstance) {
                instance = newInstance;
        }

        void doSomething() {
                instance->action();
        }

    private:

        A *instance;
};

if you do not do that, MyClass constructor will try to instantiate an A object (as it would for any member object), which is not possible since A is abstract.

Aiua
A: 

When you say

A instance;

you will create a new object of type A. But you have already said that A isa an abstract class, so you can't to that. You need to use a pointer, as several otherrs have indicated, or make A non-abstract.

anon
+9  A: 

Your problem is that you should accept a reference in your function. The reason is that a reference does not actually copy the argument passed. If you however accept an A - instead of a reference A& - then you actually copy the argument passed into the parameter object, and what you get is an object of type A - but which is actually not allowed!

    // the reference parameter will reference the actual argument
    void setInstance(A &newInstance) {
            // assign the address of the argument to the pointer member
            // instance. 
            instance = &newInstance;
    }

And then you will have to change the member in your class to be a pointer. It can't be a reference because setInstance will change what it references - a reference can only reference one object during its entire lifetime, while a pointer can be set to point do different things just by reassigning it a different address. The remaining parts look like this then

    void doSomething() {
        // call a member function on the object pointed to
        // by instance!
        instance->action();
    }

private:

    // a pointer to some object derived from A
    A *instance;

Also note that you have to compile C++ programs using g++, because it additionally links the C++ standard library to your code

g++ -o test test.cpp # instead of gcc!
Johannes Schaub - litb
Excellent, my second vtable problem was due to using gcc instead of g++. Thanks!
seanhodges
Also, if you plan on deleting a 'B' object through an 'A' pointer then define A::~A to be virtual.
Functastic
+2  A: 

I believe this is what you're trying to do. It demonstrates the polymorphism by actually printing something out depending on whether the handle class points to an instance of B or C. Others are correct that you would probably also want a virtual destructor.

This compiles with: g++ test.cpp -o Test

#include <stdio.h>

class A {
    public:
        virtual void action() = 0;
};

class B : public A {
    public:
        B() {}

        void action() {
                printf("Hello World\n");
        }
};

class C : public A {
    public:
        C() {}

        void action() {
                printf("Goodbye World\n");
        }
};

class AHandleClass {

    public:

        void setInstance(A *A_Instance) {
                APointer = A_Instance;
        }

        void doSomething() {
                APointer->action();
        }

    private:

        A *APointer;
};

int main(int argc, char** argv) {
    AHandleClass AHandle;
    B BInstance;
    C CInstance;
    AHandle.setInstance(&BInstance);
    AHandle.doSomething();
    AHandle.setInstance(&CInstance);
    AHandle.doSomething();
    return 0;
}
Robert S. Barnes
Thanks for posting this up, I attempted to compile it and it produces the same vtable errors that I am currently getting in my updated code. Any ideas?
seanhodges
At the end I accidently used -> when I should have used . It compiles now and I added some stuff to make it more informative.
Robert S. Barnes
+1  A: 

Your problem now is a linkage. For C++ program, standard C++ library has to be added:

gcc -o test -lstdc++ test.cpp

Dmitry Khalatov
Nice tip, thanks
seanhodges
You really should use g++ instead of gcc here. It's more than just -lstdc++.
jmanning2k
A: 

Dmitry is correct, you should use -lstdc++ if you use gcc, but even better is to use g++ instead. (Same syntax).
Also, you would note (I guess if you add -Wall) that you get a warning that your class with virtual functions is without a destructor, so a good idea is to add a (virtual) destructor to A as well.

E Dominique
+1  A: 

You don't have to use pointers if you resign of the setter and use a constructor. It's one of the important features in C++: base initializers in constructors often allow to avoid using pointers.

class MyClass {

        public:

                MyClass(A & newInstance) : instance(newInstance) {
                }

                void doSomething() {
                        instance.action();
                }

        private:

                A & instance;
};



int main(int argc, char** argv) {
        B myInstance;
        MyClass c(myInstance);
I was going to post something close to this. The main difference from the original to this approach is that in the original version from litb, the instance can be changed at any time calling the setter. With references, once the object is constructed it cannot 'point' to a different 'instance'
David Rodríguez - dribeas