【问题标题】:friend function cant access private memebers好友功能无法访问私人成员
【发布时间】:2021-08-05 17:49:59
【问题描述】:

我开始学习运算符重载,起初它似乎很容易,但现在在尝试制作全局函数运算符时遇到访问私有成员的问题

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();
}

我没有看到那里有错误,但它一直在 Visual Studio 项目类成员上显示是私有的(无法访问 em),如果我在 player.hpp 和 item.h 标题顺序之间切换,我可以可以访问项目私有成员,然后我无法访问 player.hpp 私有成员

任何帮助将不胜感激。

【问题讨论】:

  • 你声明了 4 个朋友。其中哪些不能访问私有成员?请发布确切的编译器错误。
  • 您的#include 指令中有一个循环,player.hpp 包括item.hitem.h 包括player.hpp。这不行,你需要消除循环。
  • @S.M.正是那个运算符函数需要两个类,所以我在不同的类中声明为朋友
  • @n.'pronouns'm。请解释更多
  • 规范的circular dependency dupe。在这种情况下,您可以在player.hpp 中转发声明item 和/或在item.h 中转发声明player

标签: c++ visual-c++ private-members friend-function


【解决方案1】:

首先,错误 #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) {
      |      ^~~~

修复很简单:

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

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

甚至

int main()

错误 #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);
      |            ^~~~~~

这更难解释。您的课程 item 取决于课程 playerplayer 取决于 item。这是编译器无法通过的。解决方案:

item.h 中替换

#include "player.hpp"

class player;

这是一个前向声明。 item 类仅在此处使用 player

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

也就是说,编译器只需要形成一个对player的引用,它不需要知道player到底是什么。这是一个常见的“技巧”:当类 A 只使用指向 B 的指针或引用时,B 的前向声明就完全足够了。另外,去掉#include,编译速度会加快一点。

  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;
      |         ^

不要使用你不懂的东西。或者更好:不要同时使用两个你不理解的东西。在移动构造函数之外很少看到移动语义。在您成为专家之前,请尽量避免在移动构造函数和移动 operator= 以外的地方使用 &&。实际上,即使您根本不使用它们,您的程序也将是完全正确的——不使用移动语义不会使程序不正确,它可能只会使其运行速度比正确使用移动语义的速度慢一些。所以,转:

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

进入

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

此外,删除 player任何 使用 &amp;&amp; 的情况下的移动构造函数,因为它什么也不移动。你所做的就是向你的膝盖射击。

错误 #4

在所有这些更改之后,编译器提出了一系列类似类型的新抱怨:

 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

这是因为你搞砸了几乎所有的朋友声明。 该修复类似于“item.hpp”中使用的修复。而不是

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

声明

class item;

然后是真正的运算符+:

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

错误 5*obj.health += tem.health;中删除*

一般性评论

  • 不要在不编译的情况下编写大量代码。如果您正在学习一门新语言/如何编程,请编写几行代码并编译。这样一来,您将始终面临 1 个,也许是两个错误,通常很容易本地化。
  • 不要一次学习几样东西。在这里,您表明您对以下内容的理解非常有限:
    • 如何处理多个源/头文件,
    • 什么是移动语义,
    • 如何使用友元声明。

我什至没有检查你的代码质量,我只是试图让它编译。

  • 如果您是 C++ 初学者,则不需要移动语义、友元声明、运算符重载。尝试使用这些功能时,您会将注意力从真正重要的目标上转移开。您无需成为 C++ 专家即可使用它。循序渐进地学习,只使用您熟悉的工具,或者一次学习一件事。

【讨论】:

  • 非常感谢你,我真的很感激你的解释,我有点忘记了我有一个多月的东西我没有编码和东西,我有点像初学者,
  • 它工作得很好,但我不明白为什么移动构造函数不起作用。
  • 移动构造函数仅用于临时对象。这就是移动语义的全部内容:减少与临时对象相关的开销。在移动构造函数中,您可以并且应该假设参数即将消失,其内容将被释放。如果是这样,this-&gt;damage = 60; 的用途是什么?为什么要在移动构造函数完成后设置一个注定要死的对象的状态?如果您不了解移动语义,请不要使用 && 引用。时期。这不会损害您的程序正确性。使用 && 的唯一原因是效率。
  • 我明白了,我想我应该学习更多关于移动语义的知识,非常感谢你,兄弟,我真的很感谢你的解释
  • 另外,不要以修改参数的方式实现operator+ :-) 你不希望a + b 可以修改它的任何参数。你需要的是operator+=。如果将代码称为 addupdate 或类似简单的名称,代码可能会更容易阅读。
猜你喜欢
  • 2021-11-15
  • 2014-08-21
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
相关资源
最近更新 更多