4

I am new to C++ and want to play around with classes.

My code

In my world there are heros and swords. Heros carry swords. This shouldn't be too hard.

// Defining swords                                                                                                                                 
class Sword
{
  // The most important thing about a sword is its length.                                                                                         
  int lenght;
public:
  // only constructor and destructor                                                                                                               
  Sword(int swordlength){
    lenght = swordlength;
  };
  ~Sword(){};
};

// defining heros (as people with magic swords)                                                                                                    
class Hero
{
  Sword magic_sword;
public:
  // each hero gets a standard sword                                                                                                               
  Hero(){
    int meters = 2;
    magic_sword = Sword(meters);
  };
  ~Hero(){};
};

int main(){
  return 0;
}

What the compiler thinks about it

When I compile this code (g++ hero.cpp) I get an error:

 In constructor 'Hero::Hero()':
20:9: error: no matching function for call to 'Sword::Sword()'
20:9: note: candidates are:
8:3: note: Sword::Sword(int)
8:3: note:   candidate expects 1 argument, 0 provided
2:7: note: constexpr Sword::Sword(const Sword&)
2:7: note:   candidate expects 1 argument, 0 provided

What I think the problem is

Compiling the code with clang++ also fails, but the error message is not as explicit, so I won't post it here.

It seems that calling the constructor Sword(meters) failed because I provided 0 instead of 1 argument. But I clearly gave it an argument (meters), so I guess I am missunderstanding something here.

What is my mistake and what can I do about it?

Kaligule
  • 630
  • 3
  • 15
  • 1
    Hero() : magic_sword(2) { /* your ctor */ } . You need to construct the sword when constructing the hero. When it enters the code in the ctor, the Hero fields are already constructed. – Andrei Damian Nov 10 '17 at 13:34

3 Answers3

7

Your Hero always has a Sword.

That means that, the moment the class is instantiated, before the Hero::Hero constructor starts running, it already has to have a "default" Sword. The compiler is complaining that it doesn't know how to make such a "default" Sword.

Now, there's multiple ways you can address this:

Using initialization lists

In the definition of a constructor, you can specify how your object's members should be initialized (before the actual constructor runs). This is different from assigning them a new value in the constructor's body (which happens in your example). To do this, specify a colon (:) after the constructor's signature followed by the member field initializers, like this:

Hero() : magic_sword(2)
{
  // do other stuff to set up your hero
}

This will cause the magic_sword to be initialized using the Sword::Sword(int) constructor right away, which means you don't need to have a default Sword::Sword() constructor.

Read more: Understanding Initialization Lists in C++

Using pointers

Do you really want your Sword to belong exclusively to your Hero? Can your Hero maybe drop a Sword, and another Hero picks it up?

In this case, you probably don't want your Hero to have a Sword as a member - instead, you'd want a pointer to a Sword that he's currently holding - which can then maybe be nullptr if he's, for example, not holding a sword!

Treeston
  • 390
  • 1
  • 7
  • Thanks for your explanation. This works. I didn't want to use pointers yet - they are scary when you come from a functional language. – Kaligule Nov 10 '17 at 13:45
  • @Kaligule if the answer solved your problem, please consider accepting it by clicking the checkmark next to the vote buttons. – Treeston Nov 10 '17 at 13:45
  • And yeah, pointers can be scary. C++ is a big pond, you'll get there eventually. – Treeston Nov 10 '17 at 13:46
3

Your Sword class does not have a constructor with no parameters. At the beginning of Hero() (before the opening {), the members that are not specified in the initializer list get default constructed.

Try this:

Hero() : magic_sword(2) {
};

Or you can add another constructor to Sword that takes no parameters.

Sword(){
    lenght = -1;
};
crashmstr
  • 28,043
  • 9
  • 61
  • 79
1

(Let me guess - you're coming from Java?)

Compiler is correct. Sword does not have a default constructor, and it's a problem.

By this point in the code,

public:
  Hero(){
    // This point.

C++ requires the magic_sword to have been constructed already. Unlike Java, there is no "null" value to use as default. The only way to construct a Sword by that point is to use a no-argument constructor, which the Sword does not have.

Of course, there is a way to make the code work. Here is the syntax:

public:
  Hero() : magic_sword(2) {

  }

This tells the compiler how to call the Sword's constructor before the Hero's constructor's body begins.

Note on your design: you likely need magic_sword to be some sort of pointer (raw, unique or refcounting). Unlike Java, magic_sword as written is not a reference to instance of Sword but an actual instance, wholly contained withing the Hero instance.

  • Thank you, that was very helpfull. (I started programming with Java 5 years ago, but I have had much more python, since). – Kaligule Nov 10 '17 at 13:48
  • Python has the same approach: instance attributes are references, not actual instances embedded in the owner, right? –  Nov 10 '17 at 13:50