9

What is the best way to model the objects in my OOP program? I understand that the classes represent not particular objects but "groups"/"generalizations" of objects. But, I need to model individual objects that are significant in my domain for example Garfield and Snoopy below, which aren't a class of object at all but an object represented with a class?

Is this the proper way to model individual objects or am i missing something?

#include <iostream>

class Animal {
public:
    Animal(const char * name) : name(name) {};
private:
    const char * name;
};

class Dog : public Animal {
public:
    Dog(const char * name) : Animal(name) {};
private:
};

class Cat : public Animal {
public:
    Cat(const char * name) : Animal(name) {};
};

class Snoopy : public Dog {
public:
    Snoopy() : Dog("Snoopy") {};
};

class Garfield : public Cat {
public:
    Garfield() : Cat("Garfield") {};
};

int main() {
    Cat* garfield = new Garfield();
    delete garfield;
    return 0;
}

Thanks!

7
  • 9
    I'm afraid your question is too vague. What makes these particular objects special? Jan 21, 2021 at 11:33
  • @SebastianRedl The Garfield and Snoopy objects are special because they are unique, there is only 1 Garfield and 1 Snoopy. Jan 21, 2021 at 11:37
  • 7
    Cat* garfield = new Cat("Garfield"); Dog* einstein=new Dog("Einstein"); - there is probably no need to create individual classes for them.
    – Doc Brown
    Jan 21, 2021 at 16:17
  • 3
    Being singletons do not make them special from the perspective of the type system. (Object instances can each have their own identity.) Only if you define specializations (as additional capabilities or override of default behavior) would you need new classes.
    – Erik Eidt
    Jan 21, 2021 at 17:30
  • @RichardBamford think a bit about what object identity in a program means. I.e. a cat instance named garfield is also unique, there is only one such instance that resides at the same space in the memory. Instead of the place in memory you could also define your own identity function, e.g. based on the name (which would exclude other cats named Garfield) or on an ID you uniquely auto-generate at creation. But for your case, the "place in space" should be a perfectly unique representation. Jan 22, 2021 at 2:12

4 Answers 4

27

Classes define behavior.

If your Garfield and Snoopy don't have different behavior from other cats and dogs, they should most definitely not have their own class.

Consider whether you can't just create these special instances at the start of your program and pass them to where they're needed.

1
  • 6
    in addition, small behaviour differences can be modelled with attributes/configuration. I.e. instead of having a fixed movement speed of 10 cat feet per second, your Garfield could only move 2 cat feet in the same time. On the other hand it could have a higher lasagna eating speed. Both controlled by respective attributes in the class that you set when creating individual object instances. (@Sebastian: feel free to incorporate if you agree/ feel it helps OP, I don't think that aspect is worth its own answer) Jan 22, 2021 at 2:08
10

I understand that the classes represent not particular objects but "groups"/"generalizations" of objects.

Classes represent types of objects. They're part of the type system.

So if Garfield is a distinct type of cat, with different properties or logic than another cat, it might be a type, and you might represent that type with a class.

If Garfield is just a cat with a particular name - and maybe other specific values for normal cat-type attributes ... it's an instance of the same class.

eg.

class Garfield: public Cat {
  double lasagnaDeficit_; // property unique to Garfield
  ...
};

but if cats already have a favourite food, it might just be:

Cat *Garfield = new Cat("Garfield", /*favourite food*/ "Lasagna");
1
  • Thank you for the reply, this has clarified my perspective of classes Jan 21, 2021 at 18:39
4

Uniqueness

You say in a comment that what makes makes Garfield different from Cat is that it's unique. That's not what a class does for you, though:

Cat* garfield = new Garfield();
Cat* garfieldTheSecond = new Garfield();
// you now have 2 Garfields

The important question is "In which way does the uniqueness matter?".

Concerning "identity", every instance of a class (every time you call new Cat) is already "unique". Even if you call new Cat("Garfield") twice, the 2 of them are apart entities; if you change data for one, the other won't be affected.

Concerning only using a name once... well first of all, if you only call new Cat("Garfield") once (and don't change names afterwards), only one cat with that name will ever exist. That's often enough already.

If you need to enforce name uniqueness, it's similar to real life. You need an "official place" that gives out cat names, and before they give out the name "Garfield", they need to check if they have already given it out. If no name should be used twice, they have to keep a list of names they give out.
In code, the quick and easy solution for this is to add a list of strings as a static variable in the class, and check against that during creation.

The horrible "Animal" example

The "Dog is a subclass of Animal" example is a staple of programming beginner tutorials. I absolutely hate it, because it completely misleads beginners. It pretends subclasses are this awesome everyday thing that can solve a lot of problems.

In reality, in the industry you rarely use it because you run into problems really quickly. At one job, we built up an application over 4 years and we used subclasses like, 3 times in there. You wouldn't make a subclass for Dog, let alone another one for a Dog with a certain name.

This is not a direct answer to your question, but before the horrible Animal example messes your understanding up like it did mine, I'd advise you to first read up on the problem by searching for "diamond problem", and then read up on the solution by searching for "composition over inheritance".

3
  • Very good to note that "uniqueness" is not guaranteed by a different type. Jan 22, 2021 at 16:26
  • That said, you would need a specific Snoopy type (with a private constructor and a static Snoopy *GetTheOneAndOnlySnoopy() factory method returning the singleton) to guarantee singularity. Jan 22, 2021 at 16:36
  • @Peter-ReinstateMonica Indeed, but I wasn't sure about bringing up singletons, because from the description it feels as if the reason behind wanting a "unique" instance is one where a singleton is fine now, but will cause issues down the road.
    – R. Schmitz
    Jan 22, 2021 at 18:23
1

Interesting idea, actually. It is true that inheritance has fallen out of fashion in the past decades, except from stateless interfaces. But a separate type has one huge advantage in strongly typed languages like C++: It allows you to express a compile-time specification that something can only be done to Snoopy. If Snoopy were just a dog by another name you would always have to perform run time checks if you cuddle(Dog *d) him or put_him_on_snoopys_hut(Dog *d). Instead, you only cuddle(Snoopy *myDarlingSnoop), and there is no mistakin' in the dark.

Such compile time guarantees are the reason why we have a strongly typed language, and why it is a code smell to pass void pointers around in this strongly typed language.

4
  • I agree, it is a large advantage to model snoopy directly. Another example is if we needed to create an object for a specific type of jet engine, it is no use modelling a generic jet engine, we want to model that particular jet engine in detail. Thats the way i see it anyway. Additionally, directly putting snoopy into the model we can provide virtual functions specific for snoopy, so dog->Put_In_Bed() would dynamic dispatch to Snoopy at run time type where we can handle it with much more detail see pastebin.com/hTr6BTrz we call pet on a Dog but it resolves to an individual snoopy :) Jan 22, 2021 at 16:54
  • @RichardBamford You put pet() on Dog and not on Animal, because you don't want to pet the Wasp - but oh, look at the poor Cat!
    – R. Schmitz
    Jan 22, 2021 at 18:54
  • @R.Schmitz Indeed! here are two solutions: non-dynamic-dispatch pastebin.com/h3uzX8eZ with dynamic dispatch pastebin.com/Rb9HC07f I'm not sure on the theoretical validity of dynamic dispatch yet :) Technically you cannot pet all Animals Jan 23, 2021 at 9:24
  • @RichardBamford You can't even pet all dogs! But you can pet snoopy, and you can express that in the language. I guess the decision to model Snoopy as a type of his own or not hinges on how different he is from other dogs. A mere variation (barks differently, has a different hang-out spot) would not warrant a separate type. Fundamental differences though -- can speak, can reason, can fly airplanes -- would. Jan 23, 2021 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.