[英]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.