具有子类数据的超类方法
Posted
技术标签:
【中文标题】具有子类数据的超类方法【英文标题】:Superclass method with subclass data 【发布时间】:2021-04-05 09:48:00 【问题描述】:早上好。 我正在学习一些关于继承和控制台操作的概念。 如您所见,我是初学者。
所以我试图在控制台上绘制一个字符,我希望更新它的位置。 现在请注意,我知道我的代码可能在多种方面都非常糟糕,并且可能有数百种更好的完全替代方法可以做到这一点,但我想首先了解一些继承概念以及为什么它不能按原样工作。
所以,我在控制台上绘制了我的玩家角色“X”,然后我通过调用特定的成员方法来更新它的位置来移动它。 现在,因为我让 Player 类扩展了 DrawConsole 类,所以我想在 Player 实例上调用 drawConsole。
当我这样做时,我让 playerA 实例的位置坐标实际更新,但对播放器实例的引用现在有两个名为“位置”的成员,如您在图像上看到的那样。 我怎么能说选择 playerA 而不完全重新编写代码或使用完全不同的方法? 或者也许只是我不能,我实际上已经完全改变了方法? 希望我能够表达我的疑问。
这里是代码
#include <ctime>
#include <cstdlib>
#include "windows.h"
#define width 100
#define height 15
class StaticBuffer
public:
StaticBuffer() srand(time(0));
void loadBackGround(CHAR_INFO *backGround, int swidth, int sheight)
for (int y = 0; y < sheight; y++)
int rnd = rand() % 100 + 1;
for (int x = 0; x < swidth; x++)
if (y == 0 || y == sheight - 1)
backGround[y * swidth + x].Char.AsciiChar = (unsigned char)127;
backGround[y * swidth + x].Attributes = (unsigned char)23;
else if (x > 4 * rnd && x < (4 * rnd) + 5 || x > 4 * rnd / 2 && x < (4 * rnd / 2) + 5)
backGround[y * swidth + x].Char.AsciiChar = (unsigned char)178;
backGround[y * swidth + x].Attributes = (unsigned char)12;
else
backGround[y * swidth + x].Char.AsciiChar = 32;
backGround[y * swidth + x].Attributes = (unsigned char)3;
private:
;
class DrawConsole
public:
DrawConsole()
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
rConsole = GetStdHandle(STD_INPUT_HANDLE);
windowSizeInit = 0, 0, 30, 10;
windowSize = 0, 0, bufferSize.X - 1, bufferSize.Y - 1;
backGround = new CHAR_INFO[bufferSize.X * bufferSize.Y];
obstacle = new CHAR_INFO[bufferSize.X * bufferSize.Y];
inputBuffer = new INPUT_RECORD[4];
drawBackGround.loadBackGround(backGround, bufferSize.X, bufferSize.Y);
nInputWritten = 0;
nOutputWritten = 0;
playerString[0] = L'X';
charLenght = 1;
position = 10,13;
void drawConsole()
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
rConsole = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleWindowInfo(wConsole, TRUE, &windowSizeInit);
wConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleScreenBufferSize(wConsole, bufferSize);
SetConsoleWindowInfo(wConsole, TRUE, &windowSize);
WriteConsoleOutputA(wConsole, backGround, bufferSize, 0,0, &windowSize);
WriteConsoleOutputCharacterW(wConsole, playerString, charLenght, position, &nOutputWritten);
void drawChar()
WriteConsoleOutputA(wConsole, backGround, bufferSize, 0,0, &windowSize);
WriteConsoleOutputCharacterW(wConsole, playerString, charLenght, position, &nOutputWritten);
protected:
HANDLE wConsole;
HANDLE rConsole;
COORD bufferSizewidth, height;
SMALL_RECT windowSizeInit;
SMALL_RECT windowSize;
CHAR_INFO *backGround;
CHAR_INFO *obstacle;
INPUT_RECORD *inputBuffer;
DWORD nInputWritten;
DWORD nOutputWritten;
DWORD charLenght;
StaticBuffer drawBackGround;
wchar_t playerString[2];
COORD position;
;
class Player :public DrawConsole
public:
Player()
position.X = 20;
position.Y = height - 2;
void movePlayerRight()
for (int i = 0; i < 3; i++)
position.X += 1;
COORD getPositionC() return position;
private:
COORD position;
;
Player *playerA = new Player;
DrawConsole *myConsole = new DrawConsole;
int main()
myConsole->drawConsole();
while (true)
//Sleep(5000);
playerA->movePlayerRight();
playerA->drawChar();
【问题讨论】:
只需从派生类中删除位置成员即可。 我需要它在 Player 实例上调用 movePlayerRight()。 "我需要它" 你需要一个职位或两个不同的职位。如果您需要一个职位,则需要一个名为“职位”的变量。删除另一个。如果您需要两个不同的位置,则需要两个变量,但是将它们都命名为“位置”是一个非常糟糕的主意。重命名另一个。如果你不能决定是想要一个职位还是两个不同的职位,那是设计问题,而不是 C++ 问题。 【参考方案1】:这取决于你真正想要什么。如果想法是两个变量代表相同的概念,则不必在派生类中重新定义它,因为它在基类中受到“保护”,因此派生类能够访问它。
如果变量代表不同的事物,但它们恰好具有相同的名称(顺便说一句,这不是一个好主意),您可以使用定义变量的类来限定它。因此,例如,你可以这样做:
DrawConsole::position.X += 1;
要修改DrawConsole
中声明的position
变量和:
Player::position.X += 1;
修改Player
中声明的position
变量
但是,正如我之前所说,我会尽量避免两个变量同名,因为这很容易导致错误。
更新:
如果您想保持继承不变,只需从Player
中删除属性position
。原因如下:
当前,当您调用drawChar
时,您正在执行DrawConsole
类中的代码(Player
本身没有定义drawChar
方法)。此代码无法访问Player::position
,因为父类中的方法无法访问子类中的属性(即使您从子类的实例调用该方法),所以它只能看到DrawConsole::position
,那就是它正在使用的变量。
但是当您在Player
的实例中调用movePlayerRigth
时,正在执行的代码是Player
类中的一个方法。此方法尝试访问位置属性,发现有两种可能性:DrawConsole::position
和Player::position
。在这种情况下,它选择Player::position
,因为它是在同一个类中定义的。
因此,您有一个基于DrawConsole::position
绘制控制台的方法和另一个修改Player::position
的方法。这是行不通的,事实上如果你运行它,你会看到 X 没有移动。
如果您从Player
中删除position
变量,在movePlayerRight
中当您尝试访问变量position
时,代码会看到Player
没有定义位置属性,但它意识到它的父类 (DrawConsole
) 确实定义了一个 position
属性,并且具有受保护的访问权限。受保护意味着子类中的代码可以直接访问它,因此movePlayerRight
将修改DrawConsole::position
。在这种情况下,drawChar
和 movePlayerRight
都将访问同一个变量,并且将按预期工作。
因此,如果您希望这样,请从 Player 类中删除以下行:
COORD position;
您将看到代码按预期编译和工作(X 向右移动),因为现在Player
中的代码和DrawConsole
中的代码正在访问同一个变量 (DrawConsole::position
)。
【讨论】:
首先感谢您的回复。我只想更新我的 playerA 实例上的坐标位置,然后调用 drawChar 使其出现在屏幕上的另一个位置。它们代表相同的东西,但问题是我必须在这个概念中同时拥有两个“位置”变量。在 playerA 实例中,我需要它,因为我希望它代表要更新的当前玩家位置。在 drawChar 我必须拥有它,因为 WriteConsoleOutputCharacterW 需要它在它的主体中。那么问题来了。 好吧,我的回答试图解决在继承中访问两个具有相同名称的变量的想法,但主要问题是你有一个从“DrawConsole”类继承的 Player 类,它实际上代表一个控制台(您甚至可以将其命名为“myConsole”)。但是播放器不是控制台,控制台包含播放器,所以 Player 应该是 DrawConsole 的一个属性,而不是子级。无论如何,如果这只是为了测试,请从 Player 中删除 position 属性,您会看到代码仍然可以正确编译,因为它将访问 DrawConsole 中的 position 属性。 我的目标是有一个在屏幕上“绘制”东西的类和另一个代表我想在屏幕上绘制的东西的类。例如,我可以制作传递参数的东西,但我想体验一些继承,这就是重点。所以我的目标是有一个可以绘制的类,但使用来自另一个类的数据,我在其他地方以某种方式操作。所以,通过这种方式,让 Player 成为 Draw 的孩子对我来说似乎不是错误的方式。假设我想在不传递参数的情况下执行此操作。我该做什么?甚至可能吗?无论如何,再次感谢您的帮助。 如果你想保持继承,那么解决方案是从 Player 中删除位置。我已经更新了答案来解释这背后的原因。 是的,您可以从子节点访问和操作父节点的数据。但请记住,只有数据标记为public
(因为那时每个人都可以访问它)或protected
(因为你是一个子类)。如果标记为private
,则无法访问。以上是关于具有子类数据的超类方法的主要内容,如果未能解决你的问题,请参考以下文章