Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why and when to pass class types in C++ by pointer?

Consider the following code:

class Abstract{
public:
    virtual void printStuff()=0;
};

class Derived: public Abstract{
public:
    void printStuff(){
        printf("Stuff\n");
    }
};

Now, let's say I want to create a function that uses the printStuff method from Abstract class. Before I learned that only one way is possible in C++, I thought that there would be two ways: the less obvious with pointers, and the more obvious, similar to what you would expect to do with ints, chars, etc.:

void ptr_function(Abstract* abs){ //non-obvious
    abs->printStuff();
}

void non_ptr_function(Abstract abs){ //obvious, analogous to, say, pow(a,b)
    abs.printStuff();
}

Now, I understand that the second one is forbidden in C++. However, I don't really understand the underlying reason for such design. Don't the above functions look identical, aside from the pointer vs. actal object being passed as an argument?

As a follow-up question: what is the preferred way to build classes that have to "contain" other, abstract, classes as one of the fields? If the answer to this question is also: "pointers", then am I missing something, or do I have to keep track of those objects' timelife myself (i.e. deleting them manually)? For non-abstract classes this is not an issue, as if I don't use pointers, then whenever that object gets out of scope, it gets automatically deleted (destructors called and so on). But if I have to use pointers, it looks like that micro-management takes a lot of unnecessary time and code.

Is there any better way to approach this?

like image 685
akrasuski1 Avatar asked Jan 13 '15 18:01

akrasuski1


1 Answers

You can't pass an abstract class by value because it can't be instantiated. In most cases pass-by-value is the wrong thing to do anyway (or at least the non-optimal). What you're looking for is pass by reference:

void ref_function(Abstract & abs)
{
    abs.printStuff();
}

When you pass by reference any modifications made to abs inside ref_function are applied to the same instance that exists outside of the function. Ideally in your test case, you'd want to pass the object as const Abstract & abs which will prevent any changes from being made to the object. In your example though, you would need to mark printStuff as const to denote that it will not change the object it's invoked on - the signature would change to virtual void printStuff() const

In response to your other question about how ownership of abstract classes should work.. remember that you can't actually have an instance of an abstract class, so what you're talking about is holding a pointer to some derived object via a handle to its abstract base class. You probably want to use std::unique_ptr for this as it will properly delete the owned object when your class is destroyed.

class Abstract
{
    public:
        virtual void Foo() = 0;
};

class Derived : public Abstract
{
    public:
        virtual void Foo() override {}
};

class MyClass
{
    public:
        MyClass();
    private:
        std::unique_ptr<Abstract> myObject;
};

MyClass::MyClass() : myObject(std::make_unique<Derived>())
{
}
like image 73
mbgda Avatar answered Nov 08 '22 04:11

mbgda