-1

i started learning about operators overloading, at first it seem to easy, but now am having a problem accessing private member when try to make a global funtion operator

player.hpp

#ifndef _PLAYER_HPP_
#define _PLAYER_HPP_

#include <iostream>
#include <string>
#include "item.h"

class player
{
    friend player operator+(player& obj, player& tem);
    static int numPlayer;
    float *health;
    int mspeed;
    int damage;
    int xp;
    std::string name;

public:
    // Constructors
    player(std::string = "player", float _health = 100, int _xp = 0);

    // Copy Constructor
    player(const player& obj);

    // Move Constructor
    player(player&& obj);

    // Functions
    void display();
    
    // Friends functions
    friend void test(player user);
    friend player operator+(player &&obj, const item &tem);
    // Diconstructors
    ~player();
};


#endif // _PLAYER_HPP_

player.cpp

#include "player.hpp"
#include "item.h"
#include <iostream>
#include <cstring>
#include <string>

int player::numPlayer = 0;

// Constructors
player::player(std::string _name, float _health, int _xp) {
    numPlayer++;
    this->health = new float;
    *this->health = _health;
    this->xp = _xp;
    this->name = _name;
    std::cout << "constructor for " << this->name << std::endl;
}

// Copy constructors
player::player(const player& obj) {
    this->health = new float;
    *this->health = *obj.health;
    this->xp = obj.xp;
    this->name = obj.name;
    std::cout << "copy constructor for " << this->name << std::endl;
}

// Move Constructors
player::player(player&& obj) {
    this->damage = 60;
    this->mspeed = 50;
    this->health = obj.health;
    this->xp = obj.xp;
    this->name = obj.name;
    obj.health = nullptr;
    std::cout << "Move constructor for " << this->name << std::endl;
}

void player::display() {
    std::cout << "========================" << std::endl
        << this->name << std::endl
        << *this->health << std::endl
        << this->xp << std::endl
        << this->damage << std::endl
        << this->mspeed << std::endl;
}

player::~player() {
    delete[] health;
    std::cout << "distruction for: " << name << std::endl;
}

void test(player user) {
    std::cout << user.name << std::endl;
}

player operator+(player&& obj, const item& tem) {
    *obj.health += tem.health;
    obj.damage += tem.damage;
    obj.mspeed += tem.ms;
    return obj;
}

item.h

#ifndef _ITEM_H_
#define _ITEM_H_
#include <iostream>
#include <string>
#include "player.hpp"

class item {
    int damage; // Bonus damage
    int health; // Bonus health
    int ms; // Bonus Movement speed
    std::string name; // item name

public:
    //constructor
    item(std::string name, int _damage = 0, int _health = 0, int _ms = 0)
        : name {name}, damage {_damage}, health{_health}, ms {_ms}{}
    
    friend player operator+(player &&obj,const item &tem);
};

#endif // _ITEM_

Main.cpp

#include <iostream>
#include <string>
#include "player.hpp"
#include "item.h"

player operator+(player&& obj, const item& tem);
void test(player user);

void main(int args, char* argv) {
    player a("YASOU96");
    item deathSword("death Sword", 150, 0, 20);
    a.display();
    a = a + deathSword;
    a.display();
}

i dont see that there is a mistake there but it keep showing on visual studio item class member are private (can't access em), nd if i switch between the player.hpp and item.h header order, i can have access to item private member, nd then i lose access on player.hpp private member

Any help would be appreciated.

  • You declare 4 friends. Which of them can't access the private members? Please post the exact compiler error. – 273K May 16 '21 at 04:41
  • You have a cycle in your `#include` directives, `player.hpp` includes `item.h` and `item.h` includes `player.hpp`. This cannot work, you need to eliminate the cycle. – n. m. could be an AI May 16 '21 at 04:44
  • @S.M. it's that operator function that takes two classes soo i declared as a friend in thoes diffrent classes – Kias Mohamed Islam May 16 '21 at 04:49
  • @n.'pronouns'm. explain more please – Kias Mohamed Islam May 16 '21 at 04:49
  • 1
    The canonical [circular dependency dupe](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes). In this case you can forward-declare `item` in `player.hpp` and/or forward-declare `player` in `item.h`. – Nathan Pierson May 16 '21 at 05:17
  • Please [edit] your code to reduce it to a [mcve] of your problem. Your current code includes much that is peripheral to your problem - a minimal sample normally looks similar to a good unit test: only performing one task, with input values specified for reproducibility. – Toby Speight May 16 '21 at 16:34
  • This doesn’t address the question, but names that begin with an underscore followed by a capital letter (`PLAYER_HPP_`, `_ITEM_H_`) and names that contain two consecutive underscores are reserved for use by the implementation. Don’t define them in your code.. – Pete Becker May 16 '21 at 21:12

1 Answers1

1

First things first, Error #1:

main.cpp:9:1: error: ‘::main’ must return ‘int’
    9 | void main(int args, char* argv) {
      | ^~~~
main.cpp:9:6: warning: second argument of ‘int main(int, char*)’ should be ‘char **’ [-Wmain]
    9 | void main(int args, char* argv) {
      |      ^~~~

The fix is easy:

int main(int args, char* argv[])

or

int main([[maybe_unused]] int args, [[maybe_unused]] char* argv[])

or even

int main()

Error #2:

In file included from player.hpp:6,
                 from main.cpp:3:
item.h:18:12: error: ‘player’ does not name a type
   18 |     friend player operator+(player &&obj,const item &tem);
      |            ^~~~~~

This is more difficult to interpret. Your class item depends on class player and player depends on item. This is impossible for the compiler to go through. Solution:

In item.h replace

#include "player.hpp"

with

class player;

This is a forward declaration. Class item uses player only here:

    friend player operator+(player &&obj,const item &tem);

that is, the compiler needs to form only a reference to a player, it needs not to have the detailed knowledge about what player actually is. This is a common "trick": when class A uses only pointers or references to B, the forward declaration of B is completely enough. Moreover, be eliminating #include, you speed up the compilation a bit.

  main.cpp: In function ‘int main(int, char*)’:
main.cpp:13:9: error: cannot bind rvalue reference of type ‘player&&’ to lvalue of type ‘player’
   13 |     a = a + deathSword;
      |         ^

Don't use things you don't understand. Or better: don't use two things you don't understand at the same time. Move semantics is rarely seen outside move constructors. Until you're an expert, try and refrain from using && in places other than the move constructor and moving operator=. Actually, even if you won't use them at all, your program will be perfectly correct - not using move semantics does not make the program incorrect, it may only render it run a bit slower that it could with move semantics being used correctly. So, turn:

    friend player operator+(player &&obj,const item &tem);

into

    friend player operator+(player &obj, const item &tem);

Also, delete the move constructor in player and any cases where you use &&, because it moves nothing. All you do is to shoot at your knee.

Error #4

After all these changes, the compiler presents a series of new complaints of similar type:

 player.cpp: In function ‘player operator+(player&&, const item&)’:
player.cpp:58:24: error: ‘int item::health’ is private within this context
   58 |     *obj.health += tem.health;
      |                        ^~~~~~
In file included from player.hpp:6,
                 from player.cpp:1:
item.h:11:9: note: declared private here
   11 |     int health; // Bonus health

This is because you messed up almost all friend declaration(s). The fix is similar to the one used in `item.hpp". Instead of

friend player operator+(player& obj,const  player& tem);

declare

class item;

and then the true operator+:

friend player operator+(player& obj, const item& tem);

Error 5 Remove * from *obj.health += tem.health;

GENERAL REMARKS

  • Don't write tons of code without compiling it. If you're learning a new language / how to program, write a few lines and compile. In this way you'll always face 1, perhaps two bugs, usually easy to localize.
  • Don't learn several things at a time. Here you've shown you have a very limited understanding of:
    • how to handle multiple source / header files,
    • what is move semantics,
    • how to use friend declarations.

and I didn't even look into the quality of your code, I just tried to made it compile.

  • If you're C++ beginner, you don't need move semantics, friend declaration, operator overloading. Trying to use these features, you're distracting your attention from really important objectives. You don't have to be a C++ expert to use it. Learn gradually and use only the tools you understand well, or learn one thing at a time.
zkoza
  • 2,644
  • 3
  • 16
  • 24
  • thank you soo much i really appreciat your explecation, well i kinda forgot some stuf i had more then one month i didnt code and stuf and am kinda a beginner in it, – Kias Mohamed Islam May 16 '21 at 14:27
  • well it works perfectly, but i cant understand why the move constructor dont work. – Kias Mohamed Islam May 16 '21 at 14:36
  • Move constructors are used ONLY on temporary objects. That's what move semantics is all about: reduce the overhead related to temporary objects. Inside the move constructor you can and should assume the argument is about to die, its contents will be freed. If so, whats use of `this->damage = 60;`? Why setting the state of an object that is doom to die once the move constructor has finished? If you don't understand move semantics, don't use && references. Period. This will not hurt your program correctness. The only reason for using && is efficiency. – zkoza May 16 '21 at 16:42
  • i see well i guess i should learn more abt move semantics then, thank u soo much bro i really appreciate your explecation <3 – Kias Mohamed Islam May 18 '21 at 03:56
  • Also, don't implement `operator+` in a way that it's arguments are modified :-) You don' expect that `a + b` can modify any of its arguments. What you need is rather `operator+=`. The code would perhaps be even easier to read if it was called as simply as `add`, `update` or something similarly simple. – zkoza May 18 '21 at 14:42
  • didnt get it explain more please – Kias Mohamed Islam May 18 '21 at 22:57
  • Your `operator+` modifies its 1st argument. This is completely counterintuitive. But for `a += b` it is expected that `a` will be modified. In this case, however, `a` is the first argument of the operator. – zkoza May 19 '21 at 01:35