在 C++ 中,如何修复指针 class 的变量在我调用它时变为 nullptr?
In C++, how do I fix a pointer class's variable becoming a nullptr when I call it?
我想在 class: class1 中使用 class: class2。根据我的阅读,为了防止循环依赖,必须在 class1.h 中转发声明 class2 并将其作为指针。在我的 class1.cpp 文件中从 class2 调用函数后。我无法调用 class2 中的变量而不会出现“无法读取内存”或 nullptr。
这是我的代码,感谢您的帮助:
//main.cpp
#include "Login.h"
#include <iostream>
using namespace std;
int main() {
Login login;
login.StartMenu();
cout << "ENDING" << endl;
system("pause");
return 0;
}
//Login.h (Class1)
#pragma once
#include <iostream>
using namespace std;
class GameManager;
class Login {
public:
void StartMenu();
private:
GameManager* manager;
};
//Login.cpp
#include "Login.h"
#include "GameManager.h"
void Login::StartMenu() {
manager->GameStart();
}
//GameManager.h (Class2)
#pragma once
class GameManager {
public:
void GameStart();
private:
int level = 1;
};
//GameManager.cpp
#include "Login.h"
#include "GameManager.h"
void GameManager::GameStart() {
cout << level;
}
通常,将 headers 之间的依赖关系保持在最低限度是个好主意,并且使用仅 forward-declared 的 class 指针是一种既定的方法.即使没有循环依赖,这也是一种很好的做法,因为它可以大大减少大型项目中的重新编译时间。
关于您的具体问题:本质上,Login
class,尤其是 Login::StartMenu
函数,需要知道要使用哪个 GameManager
实例。指向该实例的指针将存储在 manager
中。理想情况下,您可以通过 GameManager *
构造函数参数在 Login
实例的构造时告诉它:
#ifndef LOGIN_H
#define LOGIN_H
class GameManager;
/// This class handles the login procedure for a specific
/// game manager which must be provided to the constructor.
/// It cannot be copied (so it cannot be
/// in arrays) or default-constructed.
class Login {
public:
/// The constructor does nothing except initializing manager.
/// @param gmPtr is a pointer to the game manager
/// this instance is using.
void Login(GameManager *gmPtr)
: manager(gmPtr) { /* empty */ }
void StartMenu();
private:
GameManager* manager;
};
#endif // LOGIN_H
为了完整起见,以下是您将如何使用它:
#include "Login.h"
#include "GameManager.h"
#include <iostream>
using namespace std;
int main() {
GameManager gm;
Login login(&gm); // <-- provide game manager to login
login.StartMenu();
cout << "ENDING" << endl;
system("pause");
return 0;
}
如果这是不可能的,因为 GameManager
实例尚不存在,或者在构建 Login
实例期间未知(例如,如果您有一个 Login
实例,其元素必须是 default-constructed) 您可以向 Login::StartMenu
方法提供参数。但是构造函数参数更受欢迎,因为您可以确保 class 在代码的其余部分中起作用——这种“不变量”是构造函数存在的主要原因。
如果所有函数都获得该指针参数,您当然有可能根本不需要保存指针。 Login
class 是否与 GameManager
有 one-to-one 关系(在这种情况下它只是持有指向它的指针)或没有(在这种情况下每个函数都被告知每个时间)是一个设计决定。
我想在 class: class1 中使用 class: class2。根据我的阅读,为了防止循环依赖,必须在 class1.h 中转发声明 class2 并将其作为指针。在我的 class1.cpp 文件中从 class2 调用函数后。我无法调用 class2 中的变量而不会出现“无法读取内存”或 nullptr。
这是我的代码,感谢您的帮助:
//main.cpp
#include "Login.h"
#include <iostream>
using namespace std;
int main() {
Login login;
login.StartMenu();
cout << "ENDING" << endl;
system("pause");
return 0;
}
//Login.h (Class1)
#pragma once
#include <iostream>
using namespace std;
class GameManager;
class Login {
public:
void StartMenu();
private:
GameManager* manager;
};
//Login.cpp
#include "Login.h"
#include "GameManager.h"
void Login::StartMenu() {
manager->GameStart();
}
//GameManager.h (Class2)
#pragma once
class GameManager {
public:
void GameStart();
private:
int level = 1;
};
//GameManager.cpp
#include "Login.h"
#include "GameManager.h"
void GameManager::GameStart() {
cout << level;
}
通常,将 headers 之间的依赖关系保持在最低限度是个好主意,并且使用仅 forward-declared 的 class 指针是一种既定的方法.即使没有循环依赖,这也是一种很好的做法,因为它可以大大减少大型项目中的重新编译时间。
关于您的具体问题:本质上,Login
class,尤其是 Login::StartMenu
函数,需要知道要使用哪个 GameManager
实例。指向该实例的指针将存储在 manager
中。理想情况下,您可以通过 GameManager *
构造函数参数在 Login
实例的构造时告诉它:
#ifndef LOGIN_H
#define LOGIN_H
class GameManager;
/// This class handles the login procedure for a specific
/// game manager which must be provided to the constructor.
/// It cannot be copied (so it cannot be
/// in arrays) or default-constructed.
class Login {
public:
/// The constructor does nothing except initializing manager.
/// @param gmPtr is a pointer to the game manager
/// this instance is using.
void Login(GameManager *gmPtr)
: manager(gmPtr) { /* empty */ }
void StartMenu();
private:
GameManager* manager;
};
#endif // LOGIN_H
为了完整起见,以下是您将如何使用它:
#include "Login.h"
#include "GameManager.h"
#include <iostream>
using namespace std;
int main() {
GameManager gm;
Login login(&gm); // <-- provide game manager to login
login.StartMenu();
cout << "ENDING" << endl;
system("pause");
return 0;
}
如果这是不可能的,因为 GameManager
实例尚不存在,或者在构建 Login
实例期间未知(例如,如果您有一个 Login
实例,其元素必须是 default-constructed) 您可以向 Login::StartMenu
方法提供参数。但是构造函数参数更受欢迎,因为您可以确保 class 在代码的其余部分中起作用——这种“不变量”是构造函数存在的主要原因。
如果所有函数都获得该指针参数,您当然有可能根本不需要保存指针。 Login
class 是否与 GameManager
有 one-to-one 关系(在这种情况下它只是持有指向它的指针)或没有(在这种情况下每个函数都被告知每个时间)是一个设计决定。