3

I have an abstract class that has a variable owner_ that is a string. Each derived class declares the name of this variable. Is it better practice to have the variable in the abstract base class, or can I better implement it multiple times in the derived class?

#include <string>
#include <iostream>

class Pet
{
    public:
        Pet(const std::string& owner) : owner_(owner) {}
        virtual ~Pet() = 0;
        virtual void print_status() = 0;
    protected:
        const std::string owner_;
};

Pet::~Pet() {}

class Dog : public Pet
{
    public:
        Dog(const std::string& owner) : Pet(owner) {}
        ~Dog() {};
        void print_status()
        {
            std::string s = "Woof! My owner is ";
            s += owner_;
            std::cout << s << std::endl;
        }
    // Or better here?
    // private:
    //     const std::string owner_;
};

class Cat : public Pet
{
    public:
        Cat(const std::string& owner) : Pet(owner) {}
        ~Cat() {};
        void print_status()
        {
            std::string s = "Meow! My owner is ";
            s += owner_;
            std::cout << s << std::endl;
        }
    // Or better here?
    // private:
    //     const std::string owner_;
};

int main()
{
    Dog dog("Mario");
    dog.print_status();

    Cat cat("Luigi");
    cat.print_status();

    return 0;
}
Chiel
  • 6,006
  • 2
  • 32
  • 57

2 Answers2

4

IMO that's exactly what abstract base classes are for: Provide common implementations for an interface in an inheritance hierarchy.
I'd just go a step further and even separate the interface from the abstract base class:

struct IPet {
    virtual ~IPet() = {}
    virtual void print_status() = 0;
    virtual const std::string& get_owner() const = 0;
};

class Pet : public IPet
{
public:
    Pet(const std::string& owner) : owner_(owner) {}
    virtual const std::string& get_owner() const { return owner_; }
    virtual ~Pet() {} // = 0; Don't declare the destructor as pure virtual function
    virtual void print_status() = 0;
protected:
    std::string owner_;
};
user0042
  • 7,917
  • 3
  • 24
  • 39
  • Thanks, but what do I gain from this design? – Chiel Sep 05 '17 at 13:15
  • @Chiel Clear separation of interface and implementation. – user0042 Sep 05 '17 at 13:16
  • @Chiel if you have a `protected` member you need to check at least two places to make sure the invariants are not violated. Imagine you want to forbid the empty string as owner, then it is easier to make a single class responsible for making sure `owner_ == ""` is never true. If you make it protected then each derived has to take care of that – 463035818_is_not_an_ai Sep 05 '17 at 13:16
  • As above, that separation also influences on being sticky with implementation. You are providing important "clue" in the base class about for what this class is designed, what are the most important components that this class's derivatives **has to** contain etc. – Guillotine Sep 05 '17 at 13:19
  • And what would I do if `owner_` were a static constant? (Which is in fact a new question...) – Chiel Sep 05 '17 at 13:20
  • 1
    I would recommend to avoid static members in base classes, there are common patterns to solve such approach in different ways f. e. GoF Singleton pattern, this question was already answered here [SO static constant variables in abstract base class](https://stackoverflow.com/questions/13905704/static-const-variables-in-abstract-baseclass) , [Can i have static member in an abstract base class](https://stackoverflow.com/questions/6387029/can-i-have-static-data-members-in-an-abstract-class) – Guillotine Sep 05 '17 at 13:23
  • @user0042. I do not think that the reference is a good idea. What if the owner string that is used to initialize the class goes out of scope? – Chiel Sep 05 '17 at 13:24
  • @Chiel Then you have to drop the `const`. – user0042 Sep 05 '17 at 13:44
  • @Guillotine Where is a `static` member in my example? Or did you mean to respond to the OP? – user0042 Sep 05 '17 at 13:58
  • @user0042 That's respond to Chiel comment which in fact is a new question, i fully agree with your example + – Guillotine Sep 05 '17 at 14:02
1

You might want to use abstract to force your child classes to implement the method but not necessarily define anything in them. If you use them deliberately then having the owner in base class but different content in respective methods is correct.

Abstract methods are being used for example if you want all of your subclasses to at least declare the function inside their own class which is sometimes needed for the different behavior of respective subclass.

class Pet
{
public:
    Pet(const std::string& owner) :
        owner_(owner) {}
    virtual ~Pet() = 0;
    virtual void print_status() = 0;
protected:
    const std::string owner_;
};

Pet::~Pet() {}

class Dog : public Pet
{
private:
    int age;
public:
    Dog(const std::string& owner, int age) :
        Pet(owner), age(age) {}
    ~Dog() {};
    void print_status(){
        std::cout << "Woof! My owner is " << this->owner_ << 
            " and my age is " << this->age << "\n\n";
    }
};

class Cat : public Pet
{
public:
    Cat(const std::string& owner) :
        Pet(owner) {}
    ~Cat() {};
    void print_status() {
        std::cout << "Miaw, my owner is " << this->owner_ << '\n';
    }
};

int main()
{
    Dog dog("Mario", 25);
    dog.print_status();

    Cat cat("Luigi");
    cat.print_status();

    system("pause");
    return 0;
}
knoxgon
  • 1,070
  • 2
  • 15
  • 31
  • Thank you @Chiel for clearing the situation. I would like to know if the solution had helped you in any way. – knoxgon Sep 05 '17 at 13:36
  • Why do you use `->this`? This isn't required for non-templated classes, is it? For the rest, your example isn't so different than mine, or do I miss something? – Chiel Sep 05 '17 at 13:37
  • `this` is actually good practice to use. It is absolutely not necessary to use and often omitted by the programmers but I tend to use it. Sometimes using it does make your life easier. For instance, let's say you want to keep track of your private member variables and member functions inside a huge .cpp file. You may want to distinguish between function's local variable with your member variable which sometimes happens to have similar names. Other times you may reassign the same variable to itself without using `this` and that could be a bug. The compiler may sometimes detect it and warn you. – knoxgon Sep 05 '17 at 13:45
  • @VG _"`this` is actually good practice to use."_ Nope. In Java maybe. – user0042 Sep 05 '17 at 13:47
  • I find it confusing in this context. You are using `this` to distinguish between local `private` variables and `protected` inherited from a base class. If I were to use `this`, I would use it to distinguish between variables in the local and class scope. – Chiel Sep 05 '17 at 13:51
  • @user0042 I would say it is more of a personal choice to use `this`. – knoxgon Sep 05 '17 at 13:52
  • @Chiel Since owner_ is protected and can be called from inherited classes, using this does call the variable/function as you already know. As I stated, it is more of a personal choice to whether or not use `this`. Your approach is definite. The following link has a lot of people sharing their opinions about `this`. You may find it useful. https://stackoverflow.com/questions/9590820/should-i-use-this-within-a-class – knoxgon Sep 05 '17 at 13:55
  • @VG. But I still don't get your philosophy. You use it for an inherited protected variable such as `owner_`, but NOT for private variable `age` which is one also is a member property of `this`. I do not see the logics of that. – Chiel Sep 05 '17 at 14:06
  • 1
    @Chiel Ah I'm sorry, I must have missed that :) I was actually in kind of a hurry when I wrote that. Didn't really pay huge attention to that. Yes, I use it on private members as well. Correction incoming! – knoxgon Sep 05 '17 at 14:09