具有子类数据的超类方法

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::positionPlayer::position。在这种情况下,它选择Player::position,因为它是在同一个类中定义的。

因此,您有一个基于DrawConsole::position 绘制控制台的方法和另一个修改Player::position 的方法。这是行不通的,事实上如果你运行它,你会看到 X 没有移动。

如果您从Player 中删除position 变量,在movePlayerRight 中当您尝试访问变量position 时,代码会看到Player 没有定义位置属性,但它意识到它的父类 (DrawConsole) 确实定义了一个 position 属性,并且具有受保护的访问权限。受保护意味着子类中的代码可以直接访问它,因此movePlayerRight 将修改DrawConsole::position。在这种情况下,drawCharmovePlayerRight 都将访问同一个变量,并且将按预期工作。

因此,如果您希望这样,请从 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,则无法访问。

以上是关于具有子类数据的超类方法的主要内容,如果未能解决你的问题,请参考以下文章

Java多态如何调用子类对象的超类方法

在 Python 的超类中调用超类方法

为啥子类可以访问具有内部类的超类的私有成员?

使用“this”调用超类方法的子类

从超类调用子类的方法

如何让两个 NSManagedObject 类具有相同的超类