繁体   English   中英

C++ 对于这种情况,朋友是个好主意吗?

[英]C++ Is friend a good idea for this scenario?

我在 C++ header 中声明了以下 2 个类:

class DrawableChunk {
    friend class Drawable;
private:
    std::queue<uint> freePos;
    std::set <Drawable*, DrawableChunkCompare> drawables;
    uint ID;
    GLuint VAO;
    GLuint VBO;
    GLuint EBO;
    long long GetMinDepth();
    long long GetMaxDepth();
    static uint globalID;
    static std::unordered_map<uint, DrawableChunk*> mapChunk;
    static std::vector<DrawableChunk*> vecChunk;

    static uint Create();
    static void InsertDrawableAndPopQueue(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue);
    static Drawable* InsertDrawableAndEvictDMax(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue);
    static uint GetNumChunks();
    static void AddDrawable(Drawable* drawable);
    static void RemoveDrawable(Drawable* drawable);
    static void AddDrawableData(Drawable* drawable);
public:
    static void PrintMap();
    static GLuint GetVAO(uint _id);
};

class Drawable {
    friend class DrawableChunk;
public:
    static const uint chunkSize{ 65536 };
    static const uint vertexDataSize{ 80 };
    static const uint elementDataSize{ 6 };
private:
    long long depthID;
    ulong ID;
    GLfloat x;
    GLfloat y;
    GLfloat xOrigin;
    GLfloat yOrigin;
    GLfloat angle;
    GLfloat xScale;
    GLfloat yScale;
    GLuint shader;
    GLuint color;
    uint chunkID;
    uint chunkPos;
    int depth;
    Sprite* sprite;
    uint subImage;
    bool update;
    static ulong globalID;

    static std::vector<Drawable*> vertexUpdateContainer;
    static std::unordered_map<Drawable*, std::array<GLubyte, vertexDataSize>> mapVertexData;

    static long long CalcDepthID(Drawable* drawable);
    void CheckAndAddToVertexUpdateContainer();
    void UpdateVertices();
    std::array<GLubyte, vertexDataSize>& GetVertexData();
public:

    Drawable(Sprite* _sprite, GLfloat _x, GLfloat _y, GLfloat _xOrigin, GLfloat _yOrigin, GLfloat _angle, GLfloat _xScale, GLfloat _yScale, GLuint _color, int _depth, GLuint _shader);

    static Drawable* Create(Sprite* _sprite, GLfloat _x, GLfloat _y, int _depth);
    static void UpdateVerticesForAll();
    static void CleanVertexUpdateContainer();

    Pointf GetPosition();
    Pointf GetOrigin();
    Pointf GetScale();
    ulong GetID();
    GLfloat GetAngle();
    GLuint GetColor();
    GLuint GetShader();
    uint GetChunkID();
    uint GetChunkPos();
    uint GetSubImage();
    int GetDepth();
    Sprite* GetSprite();
    long long GetDepthID();
    
    void SetSubImage(uint _subimage);
    void SetPosition(float _x, float _y);
    void SetOrigin(float _xOrig, float _yOrig);
    void SetScale(float _xScale, float _yScale);
    void SetAngle(float _angle);
    void SetColor(uint _color);
    void SetDepth(int _depth);
};

正如你所看到的,我交了彼此的朋友,这背后的原因是:这两个类相互依赖:创建一个drawable会将它放入一个块中,将一个drawable添加到一个块中会改变块的状态,但也可能更改其他 Drawable 的状态。 如果没有交友,我将被迫公开几个方法,但这些方法实际上是在非常特定的情况下调用的,没有任何意义,也不应该在它们非常特定的目的之外使用。

这让我最近几天很头疼。 我一直在读到交友课程是非常糟糕的做法,并且源于糟糕的设计。 然而,这些实际上是两个相互补充的独立实体。 我可能会“清算” DrawableChunk 并将所有内容插入 Drawable 中,但这真的值得吗? 这些类真的非常紧密耦合。

我已经阅读过诸如何时应该在 C++ 中使用“朋友”之类的主题? ,我只是不确定我的具体设计是否属于“你可能应该使用朋友”或“你完全错了”的类别。

因此,鉴于现在 Drawable 可能会改变 Chunk 的状态,反之亦然,您将如何重新安排这个问题? 你会让他们成为彼此的朋友吗? 你有什么建议?

编辑:提供进一步的说明。 当一个Drawable被创建时,它被添加到一个DrawableChunk(为简单起见是Chunk)。 每个 Drawable 必须跟踪它属于哪个 Chunk 以及在哪个 position。 所以当一个 Drawable 被添加到一个块时,它的 chunkID 和 chunkPos 成员必须被更新。

DrawableChunk::AddDrawable(Drawable* drawable) function 执行测试以查看 Drawable 适合哪个 Chunk,或者,如果需要,创建一个新的 Chunk。 当找到合适的 Chunk ID 和 position 时,将 Drawable 指针插入到该 Chunk 的容器(一组)中,并且必须根据该插入更新 Drawables chunkID 和 chunkPos。

EDIT2:我剥离了所有 OpenGL 功能,只留下了相关部分。 此代码应编译和 output 信息块及其内容。

图形.h

#pragma once

#include <vector>
#include <unordered_map>
#include <string>
#include <queue>
#include <set>

typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned long ulong;
typedef unsigned long long ulonglong;

class Drawable;

struct DrawableChunkCompare {
    bool operator()(Drawable* const lhs, Drawable* const rhs) const;
};

class DrawableChunk {
    friend class Drawable;
private:
    std::queue<uint> freePos;
    std::set <Drawable*, DrawableChunkCompare> drawables;
    uint ID;
    long long GetMinDepth();
    long long GetMaxDepth();
    static uint globalID;
    static std::unordered_map<uint, DrawableChunk*> mapChunk;
    static std::vector<DrawableChunk*> vecChunk;

    static uint Create();
    static void InsertDrawableAndPopQueue(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue);
    static Drawable* InsertDrawableAndEvictDMax(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue);
    static uint GetNumChunks();
    static void AddDrawable(Drawable* drawable);
    static void RemoveDrawable(Drawable* drawable);
    static void AddDrawableData(Drawable* drawable);
public:
    static void PrintMap();
};

class Drawable {
    friend class DrawableChunk;
public:
    static const uint chunkSize{ 16 };

private:
    long long depthID;
    ulong ID;
    uint chunkID;
    uint chunkPos;
    int depth;
    static ulong globalID;
    static long long CalcDepthID(Drawable* drawable);
public:

    static Drawable* Create(float _x, float _y, int _depth);

    uint GetChunkID();
    uint GetChunkPos();
    long long GetDepthID();
    void SetDepth(int _depth);
};

long long SgnLLNZ(long long num);

图形.cpp

#include "graphics.h"
#include "math_aux.h"

#include <iostream>


typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long long ulonglong;

std::unordered_map<uint, DrawableChunk*> DrawableChunk::mapChunk{};
std::vector<DrawableChunk*> DrawableChunk::vecChunk{};

uint DrawableChunk::globalID{ 0 };
ulong Drawable::globalID{ 0 };

// DrawableChunk ======================================================

uint DrawableChunk::Create() {
    DrawableChunk* ch{ new DrawableChunk };
    ch->ID = ++globalID;

    //Creating various OpenGL objects..

    for (uint i{ 0 }; i < Drawable::chunkSize; ++i) {
        ch->freePos.push(i);
    }

    mapChunk.emplace(globalID, ch);
    vecChunk.push_back(ch);
    return globalID;
}

void DrawableChunk::AddDrawable(Drawable* drawable) {
    std::queue<Drawable*> queue{};
    queue.push(drawable);

    std::vector<DrawableChunk*> chunksWithFreeSpots{};
    if (vecChunk.size() > 0) {
        for (auto& it : vecChunk) {
            DrawableChunk* ch{ it };
            if (ch->freePos.size() > 0) {
                chunksWithFreeSpots.push_back(ch);
            }
        }
    }
    int phase{ 0 };

    DrawableChunk* chunkA{ nullptr };
    DrawableChunk* chunkB{ nullptr };

    while (queue.size() > 0) {
        //std::cout << queue.front() << ", " << phase << std::endl;

        Drawable* dr{ queue.front() };

        switch (phase) {
        case 0: //Any chunks at all?
        {
            if (mapChunk.size() == 0) {
                DrawableChunk* ch{ mapChunk[Create()] };
                InsertDrawableAndPopQueue(dr, ch, queue);
            }
            else {
                phase = 1;
            }
        }
        break;
        case 1: //Any free spots?
        {
            if (chunksWithFreeSpots.size() > 0) {
                phase = 2;
            }
            else {
                phase = 3;
            }
        }
        break;
        case 2: //dr fits in any chunk's empty spot OR only one chunk?
        {
            DrawableChunk* ch{ nullptr };
            long long drDepth{ dr->depthID };
            for (auto& it : chunksWithFreeSpots) {
                if (vecChunk.size() == 1 || (drDepth >= it->GetMinDepth() && drDepth <= it->GetMaxDepth())) {
                    ch = it;
                    break;
                }
            }
            if (ch != nullptr) {
                InsertDrawableAndPopQueue(dr, ch, queue);
            }
            else {
                phase = 3;
            }
        }
        break;
        case 3: //dr fits within any chunk's range?
        {
            DrawableChunk* ch{ nullptr };
            long long drDepth{ dr->depthID };
            for (auto it : vecChunk) {
                if (drDepth >= it->GetMinDepth() && drDepth < it->GetMaxDepth()) {
                    ch = it;
                    break;
                }
            }
            if (ch != nullptr) {
                queue.push(InsertDrawableAndEvictDMax(dr, ch, queue));
                phase = 0;
            }
            else {
                phase = 4;
            }
        }
        break;
        case 4: //D fits between two chunks A and B? (check from back to front)
        {
            if (vecChunk.size() > 1) {
                chunkA = nullptr;
                chunkB = nullptr;
                long long drDepth{ dr->depthID };
                for (long long i{ static_cast<long long>(vecChunk.size()) - 2 }; i >= 0; --i) {
                    if (drDepth >= vecChunk[i]->GetMaxDepth() && drDepth <= vecChunk[i + 1]->GetMinDepth()) {
                        chunkA = vecChunk[i];
                        chunkB = vecChunk[i + 1];
                        break;
                    }
                }
                if (chunkA != nullptr) {
                    phase = 5;
                }
                else {
                    phase = 6;
                }
            }
            else {
                phase = 6;
            }
        }
        break;
        case 5: //A has a free spot?
        {
            if (chunkA->freePos.size() > 0) {
                InsertDrawableAndPopQueue(dr, chunkA, queue);
            }
            else { //B has a free spot?
                if (chunkB->freePos.size() > 0) {
                    InsertDrawableAndPopQueue(dr, chunkB, queue);
                }
                else {
                    if (chunkB->GetMaxDepth() == dr->GetDepthID()) {
                        DrawableChunk* ch{ mapChunk[Create()] };
                        InsertDrawableAndPopQueue(dr, ch, queue);
                    }
                    else {
                        queue.push(InsertDrawableAndEvictDMax(dr, chunkB, queue));
                        phase = 0;
                    }
                }
            }
        }
        break;
        case 6: //dr depth < First chunks min depth?
        {
            DrawableChunk* chFirst{ vecChunk[0] };
            if (dr->GetDepthID() < chFirst->GetMinDepth()) {
                if (chFirst->freePos.size() > 0) {
                    InsertDrawableAndPopQueue(dr, chFirst, queue);
                }
                else {
                    queue.push(InsertDrawableAndEvictDMax(dr, chFirst, queue));
                    phase = 0;
                }
            }
            else {
                DrawableChunk* chLast{ vecChunk[vecChunk.size() - 1] };
                if (chLast->freePos.size() > 0) {
                    InsertDrawableAndPopQueue(dr, chLast, queue);
                }
                else {
                    DrawableChunk* ch{ mapChunk[Create()] };
                    InsertDrawableAndPopQueue(dr, ch, queue);
                }
            }
        }
        break;
        }


    }

    //DrawableChunk::PrintMap();
}

void DrawableChunk::AddDrawableData(Drawable* drawable) {
    DrawableChunk* ch{ mapChunk[drawable->chunkID] };
    uint pos{ drawable->GetChunkPos() };
}

void DrawableChunk::RemoveDrawable(Drawable* drawable) {
    DrawableChunk* ch{ mapChunk[drawable->chunkID] };
    auto it{ ch->drawables.find(drawable) };
    ch->drawables.erase(it);
    ch->freePos.push(drawable->GetChunkPos());
}

long long Drawable::CalcDepthID(Drawable* drawable) {
    long long Ldepth{ (long long)(drawable->depth) };
    long long result{ (Ldepth << 44) + ((long long)(SgnLLNZ(Ldepth)) * drawable->ID) };
    return result;
}

long long DrawableChunk::GetMinDepth() {
    auto it{ drawables.begin() };
    if (it != drawables.end()) {
        return (*it)->GetDepthID();
    } else {
        return std::numeric_limits<long long>::min();
    }
}

long long DrawableChunk::GetMaxDepth() {
    auto it{ drawables.rbegin() };
    if (it != drawables.rend()) {
        return (*it)->GetDepthID();
    } else {
        return std::numeric_limits<long long>::max();
    }
}

bool DrawableChunkCompare::operator()(Drawable* lhs, Drawable* rhs) const {
    return (lhs->GetDepthID() < rhs->GetDepthID());
}

uint DrawableChunk::GetNumChunks() {
    return static_cast<uint>(mapChunk.size());
}

Drawable* DrawableChunk::InsertDrawableAndEvictDMax(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue) {
    auto it{ chunk->drawables.rbegin() };
    Drawable* evicted{ *it };
    chunk->drawables.erase((++it).base());
    drawable->chunkID=chunk->ID;
    drawable->chunkPos=evicted->chunkPos;
    chunk->drawables.insert(drawable);
    AddDrawableData(drawable);
    queue.pop();
    return evicted;
}

void DrawableChunk::InsertDrawableAndPopQueue(Drawable* drawable, DrawableChunk* chunk, std::queue<Drawable*>& queue) {
    drawable->chunkID=chunk->ID;
    drawable->chunkPos=chunk->freePos.front();
    chunk->freePos.pop();
    chunk->drawables.insert(drawable);
    AddDrawableData(drawable);
    queue.pop();
}

void DrawableChunk::PrintMap() {
    for (auto& it : mapChunk) {
        std::cout << "Chunk ID: " << it.first << " free spots: ";
        std::queue<uint> f{ it.second->freePos };
        std::string str{ "" };
        while (f.size() > 0) {
            str += std::to_string(f.front())+",";
            f.pop();
        }
        std::cout << str << std::endl;
        DrawableChunk* ch{ it.second };
        for (auto it2 : ch->drawables) {
            std::cout << "(ID "<<it2->ID<<" ["<<it2<<"] D "<<it2->GetDepthID() <<" "<<"pos:"<<it2->chunkPos<< ") ";
        }
        std::cout << std::endl;
    }
}

Drawable* Drawable::Create(float _x, float _y, int _depth) {
    Drawable* dr{ new Drawable() };
    dr->ID=++globalID;
    dr->depth = _depth;
    dr->depthID = CalcDepthID(dr);
    DrawableChunk::AddDrawable(dr);
    return dr;
}

// Drawable ======================================================


void Drawable::SetDepth(int _depth) {
    DrawableChunk::RemoveDrawable(this);
    depth = _depth;
    depthID = CalcDepthID(this);
    DrawableChunk::AddDrawable(this);
}

uint Drawable::GetChunkID() {
    return chunkID;
}
uint Drawable::GetChunkPos() {
    return chunkPos;
}

long long Drawable::GetDepthID() {
    return depthID;
}

long long SgnLLNZ(long long num) {
    return +1 | (num >> (sizeof(long long) * 8 - 1));
}

主文件

#include "graphics.h"

#include <iostream>
#include <vector>

typedef unsigned int uint;
typedef unsigned char uchar;

std::vector<Drawable*> drawableContainer{};

int main()
{

    for (uint i{ 0 }; i < 24; ++i) {
        int r{ rand() % 500 };
        Drawable* e1 {Drawable::Create((float)((rand() % 800)), (float)((rand() % 600)), r)};
        drawableContainer.push_back(e1);
    }

    DrawableChunk::PrintMap();

    
    return 0;
}

不,朋友更适合在顶层实施,对于简单的任务,父子是理想的。 如何实现它就像描述它需要做什么一样简单“处理着色器列表的结构”。

您的代码中所有“不好”的部分都可以通过使用 c++ 拥有的工具来修复,而不是使不需要的部分复杂化。

对代码的第一个价格进行快速重构,添加stdint.h的使用并删除 private 的使用,使其看起来更好但并不完美。 代码更多的是给出一个想法而不是替换你的。

// This header is included 
// in all std libraries and
// defines all uint32_t like
// variables types
#include <cstdint>

class DrawableChunk {

    public:

        bool update;
        Sprite * sprite;

        int32_t depth, subImage;
        uint32_t chunkID, chunkPos;
        uint64_t ID, depthID, globalID;

        GLuint shader, color;
        GLfloat x, y, xOrigin, yOrigin;
        GLfloat angle, xScale, yScale;

        uint32_t chunkSize{ UINT16_MAX };
        uint32_t vertexDataSize{ 80 };
        uint32_t elementDataSize{ 6 };

        DrawableChunk(Sprite * _sprite, GLfloat _x, GLfloat _y, 
                      GLfloat _xOrigin, GLfloat _yOrigin, 
                      GLfloat _angle, GLfloat _xScale, GLfloat _yScale, 
                      GLuint _color, int32_t _depth, GLuint _shader);

        // ~DrawableChunk(); // missing (?)
        // DrawableChunk::Create() conteins a new but i see no delete
        DrawableChunk * Create(Sprite* _sprite, GLfloat _x, GLfloat _y, int32_t _depth);

        int64_t CalcDepthID(Drawable* drawable);
        void UpdateVertices();
        void UpdateVerticesForAll();
        void CleanVertexUpdateContainer();
        void CheckAndAddToVertexUpdateContainer();

};


class Drawable {

    public:

        std::queue<uint32_t> freePos;
        std::vector<DrawableChunk *> vecChunk;

        uint32_t ID, globalID;
        GLuint VAO, VBO, EBO;

        int64_t GetMinDepth();
        int64_t GetMaxDepth();

        // can be replaced by std::vector functions
        uint32_t GetNumChunks();
        uint32_t Create();
        uint32_t create_chunk(Sprite* _sprite, GLfloat _x, GLfloat _y, int32_t _depth); // gives the arguments to the child after checks
        
        std::unordered_map<uint32_t, DrawableChunk*> mapChunk;
        std::set <DrawableChunk*, DrawableChunkCompare> drawables;

        void PrintMap();
        void AddDrawable();
        void RemoveDrawable();
        void AddDrawableData();

        // Drawable* drawable / std::queue<Drawable*>& queue, is already accesible
        void InsertDrawableAndPopQueue(DrawableChunk* chunk);
        DrawableChunk * InsertDrawableAndEvictDMax(DrawableChunk* chunk);

};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM