4

I am confused about how to correctly construct c++ objects.

I have this class:

////////////////////////
// "PlayerStats.h"
//
// This class is responsible for maintaining each
//   player's stats for a given tournament and simulating
//   how these stats change as the player interacts with
//   other players.
typedef double Elo;
class PlayerStats
{
    double expectedScore(PlayerStats b) const;
    Elo elo;
    int wins;
    int losses;
    int ties;

  public:
    PlayerStats() : elo(1000), wins(0), losses(0), ties(0) {}
    PlayerStats(Elo elo) : elo(elo), wins(0), losses(0), ties(0) {}
    PlayerStats(Elo elo, int wins, int losses, int ties) : elo(elo), wins(wins), losses(losses), ties(ties) {}

    friend std::ostream &operator<<(std::ostream &os, const PlayerStats &ps);
};

// render these stats to the stream in the format:
// <elo (rounded integer)> (<wins>-<losses>-<ties>)
std::ostream &operator<<(std::ostream &os, const PlayerStats &ps)
{
    os << (int) (ps.elo + 0.5);  // round elo and out put it
    os << " " << ps.wins << "-" << ps.losses << "-" << ps.ties;
    return os;
}

When I construct it this way in main(),

int main()
{

    PlayerStats stats(1000);
    std::cout << stats << std::endl;

    return 0;
}

I get my expected result 1000 0-0-0, but when I try to call the other constructor,

int main()
{

    PlayerStats stats();
    std::cout << stats << std::endl;

    return 0;
}

I just get the integer 1 printed out which I suspect is a garbage value. It there some error I'm overlooking?

  • I can't see a good reason yet for these results, but I would start with a debugger and place breakpoints in the parameter-less constructor and in the `<<` overload, look at the values in your `PlayerStats` object and narrow things down from there – alter_igel Jan 25 '18 at 20:54
  • 4
    `PlayerStats stats();` is a function declaration. It declares a function named `stats` that returns a `PlayerStats` and has no arguments. See [most vexing parse](https://stackoverflow.com/questions/1424510/most-vexing-parse-why-doesnt-a-a-work). – François Andrieux Jan 25 '18 at 20:54
  • 1
    You don't need all that code to show the problem. Most of the code posted is irrelevant. You should aim for a [mcve]. – juanchopanza Jan 25 '18 at 20:56
  • @juanchopanza he did just that by giving a compilable example of what works and what doesn't work. Although I wasn't fast enough to answer before others, this code allowed me to copy it and try it myself. – Treyten Carey Jan 25 '18 at 21:00
  • 1
    @TreytenCarey No. As I said, most of the code is irrelevant. That isn't a [mcve]. – juanchopanza Jan 25 '18 at 21:01
  • @juanchopanza No. As I said, his code is fine. Is this seriously a lot of code for you to look through? -1. He clearly didn't know if the issue was in his class's operator overload, or in his use of the object. – Treyten Carey Jan 25 '18 at 21:15
  • 3
    The beauty of the MCVE is as you approach one you have less code. Less code means a smaller surface area in which the bug can hide. Eventually there is too little code remaining for the bug to hide and the bug is exposed. Usually you get about 3/4 of the way to a MCVE, groan, and fix the bug, but sometimes you can completely isolate the sucker still not know how to resolve it. This bug is one that I'd expect even if a novice C++ programmer got to the MCVE `#include class PlayerStats { PlayerStats(){cout << "Hello!"; } }; int main() { PlayerStats stats(); }` they'd still need help – user4581301 Jan 25 '18 at 21:19
  • 2
    @TreytenCarey Part of the point of a [mcve] is isolating the problem. It requires some effort, but it is easy enough to produce. It is one of the things that is encouraged, even expected here. I don't even have to look through OP's code, I can spot the issue immediately. But that doesn't help OP to become a better programmer. Producing a [mcve] does. – juanchopanza Jan 25 '18 at 21:25
  • Do not assume that the "they'd still need help" at the end of my previous comment means they shouldn't try. If you do not use divide and conquer attacks to assist in debugging, your career as programmer will be unproductive and short. Also use the debugger that came with your development environment. Attempting to step into `PlayerStats stats();` with a debugger to see what's going wrong should set off alarm bells. – user4581301 Jan 25 '18 at 21:38

2 Answers2

11

PlayerStats stats() declares a function named stats that returns a PlayerStats and takes no arguments. This is an instance of what is known as the "most vexing parse"; basically, any time that the compiler can interpret a statement as a function declaration, then it must do so. Understandably confusing.

To get around this, use:

PlayerStats stats;

or:

PlayerStats stats{};
0x5453
  • 12,753
  • 1
  • 32
  • 61
3
 PlayerStats stats();

declares a function. To call the default constructor use

 PlayerStats stats;

This is called the most vexing parse

Andreas H.
  • 5,557
  • 23
  • 32