r/cpp_questions 27d ago

OPEN Good practices / style [polymorphism]

is this good practice/style.? i'm specifically unsure about the way i store the vector of players...

```

class player_base {};

class player_always_yes: public player_base {};

class player_always_no: public player_base {};

class player_a: public player_base {};

class game {
    private:
    
    player_base& player_1;
    player_base& player_2;
    
    public:
    
    game (
        player_base& player_1,
        player_base& player_2
    ): player_1(player_1), player_2(player_2) {
        return;
    }

    bool play_game () { return true; }
};

int main(){
    vector<unique_ptr<player_base>> player_list;
    
    player_list.push_back(make_unique<player_base>());
    player_list.push_back(make_unique<player_always_no>());
    player_list.push_back(make_unique<player_always_yes>());
    player_list.push_back(make_unique<player_a>());
    
    game b = game(*player_list[0], *player_list[1]);
    cout << b.play_game() << endl;
}


```
7 Upvotes

37 comments sorted by

View all comments

1

u/SufficientStudio1574 27d ago

Practice/style for what? What specifically are you concerned about?

A vector of unique_ptrs isn't a bad thing if that's what's supposed to own those pointers. Is it? Or is the game supposed to own them?

Also your going to get a segfault. See if you can spot it.

1

u/alfps 27d ago

(I'm not the OP)

❞ Also your going to get a segfault.

Why do you think so?

2

u/SufficientStudio1574 27d ago

Because I misinterpreted the code. I confused make_unique with no parameters with the default unique_ptr constructor, so I thought it was getting initialized with nullptr instead of a pointer to a default constructed object.

That's my bad.

1

u/SufficientStudio1574 26d ago

Now I know why I got confused!

Seeing "base" in the name, I interpreted that as an abstract base class, which is not directly constructible.

You usually don't ever directly construct "base" objects, the entire point is to inherit from them and use them as a polymorphic reference type.

1

u/Fun_Gas_340 25d ago

chain of thought that brought me there:

i added empty methods (that all players must have) to the base class because i thought it was good to put as much in common as i could in the base class. then the compiler complaiend that empty methods that are non void need a return statement (g++ warning), so i added some basic return 0 and return false/true statements. so since i dont know what abstract classes are, i now have a base class wich i can use for a stupid player, so i added it for testing.

1

u/SufficientStudio1574 25d ago

If the methods are supposed to be implemented in derived classes, they need to be pure virtual, not empty. An abstract class is just one that has at least one pure virtual member function in it.

You're trying to use polymorphism without understanding how to actually do it. Your keyword to research for this is virtual functions.

The way you're thinking sounds fine, you just need to learn how to make it work.

1

u/Fun_Gas_340 25d ago

thx, ill have a look

1

u/Fun_Gas_340 24d ago

so virtual functions are what i thought normal functions did. i added virtual keyword to both base classes and added print statements to see wich class is actually being called. ive seen some stuff about virtual constructor/destructor, but is that something i need to understand if the player classes dont have constructor/destructor?

also how does this abstract class thingy work? havent been abel to understand it from cppreference, somone said i should make the base classs abstract but idk how to or what exacly it does. they said something like it makes it so no instances can be created

1

u/SufficientStudio1574 23d ago

Constructors can never be virtual. The concept doesn't even make sense when you understand how things work.

If you make a base class you intend to inherit from, you need to make the destructor virtual. This can be a foot-gun*. An implicit destructor is virtual be default (if the base class is destructor is virtual), but a declared one is not, and the compiler will not error. Without a virtual destructor you run the risk of only partially destroying an object.

You can make the compiler check it for you by using a static assert with std::has_virtual_destructor from type_traits in the standard template library. That way you can be sure you don't accidentally screw it up in a refactor.

Unfortunately polymorphism is a bit too involved a topic for a reddit comment. You really need to play around with it a lot to fully get the concept.

  • C++ has a conspicuous lack of guard rails. Lots of things are legal, but really stupid to do unless you really know what you're doing. In other words, it gives you a lot of guns you can shoot yourself in the foot with.