为班级成员使用unique_ptr

时间:2020-06-07 16:59:16

标签: c++ smart-pointers raii

我正在实现C ++程序以从TGA文件读取图像。我得到了标题的结构。

struct TGA_HEADER
{
// length of id string
char    id_length;

// image storage info
char    colour_map_type;
char    image_type;

// colour Map
short   first_entry;
short   num_entries;
char    bits_per_entry;

// image description
short   x_origin;
short   y_origin;
short   width;
short   height;
char    bits_per_pixel;
char    descriptor;
};

图像类如下:

class image
{
private:

public:
TGA_HEADER header;
std::unique_ptr<std::vector<char>> pixel_data;

image(const std::string& image_path);   
~image();

static void save_image(const std::string& file_name, image& image);
static void load_image(const std::string& path, const std::unique_ptr<std::vector<char>>& pixel_data, TGA_HEADER& header);
}; 

我的图像类的构造函数:

image::image(const std::string& image_path) 
: 
pixel_data(new std::vector<char>)
{
load_image(image_path, this->pixel_data, this->header);
}

我的加载类:

void image::load_image(const std::string& path, const std::unique_ptr<std::vector<char>>& pixel_data, TGA_HEADER& header)
{
std::ifstream file_stream(path, std::ios::in | std::ios::binary | std::ios::ate);

if (file_stream.is_open())
{
    file_stream.seekg(0, std::ios::beg);

    int tgaDesc = 0;

    /* read header */
    file_stream.read(&header.id_length, sizeof(header.id_length));
    file_stream.read(&header.colour_map_type, sizeof(header.colour_map_type));
    file_stream.read(&header.image_type, sizeof(header.image_type));

    file_stream.read((char*)(&header.first_entry), sizeof(header.first_entry));
    file_stream.read((char*)(&header.num_entries), sizeof(header.num_entries));
    file_stream.read(&header.bits_per_entry, sizeof(header.bits_per_entry));

    file_stream.read((char*)(&header.x_origin), sizeof(header.x_origin));
    file_stream.read((char*)(&header.y_origin), sizeof(header.y_origin));
    file_stream.read((char*)(&header.width), sizeof(header.width));
    file_stream.read((char*)(&header.height), sizeof(header.height));
    file_stream.read(&header.bits_per_pixel, sizeof(header.bits_per_pixel));
    file_stream.read(&header.descriptor, sizeof(header.descriptor));

    // Skip the ID String
    char* skip = new char[256];
    file_stream.read(skip, header.id_length);

    // Skip the colour map if it doesn't exist
    if (!(tgaDesc & 4))
    {
        int colourMapSize = header.colour_map_type * header.num_entries;
        file_stream.read(skip, colourMapSize);
    }

    delete skip;

    int imageDataSize = header.width * header.height * (header.bits_per_pixel / 8);
    pixel_data->resize(imageDataSize);

    int originalPosition = (int)file_stream.tellg();

    /* read image data */
    file_stream.read(pixel_data->data(), imageDataSize);
}

我的第一个问题是,如果我以正确的方式(尤其是在构造函数中)将unique_ptr用于我的pixel_data,第二个问题是我是否分别正确地初始化了TGA_HEADER对象如果我必须手动删除它或使用智能指针。

我对C ++还是很陌生,所以请随时评论可能遇到的其他问题。

最诚挚的问候

2 个答案:

答案 0 :(得分:2)

您在这里使用std::unique_ptr在技术上没有任何问题,但是在这种情况下完全没有必要。 std::vector本质上已经是指向基础数组的唯一指针。与其让您的班级有一个std::unique_ptr<std::vector<char>>成员,不如让它只有一个std::vector<char>成员。


我也将质疑为什么load_imagesave_image是静态的,如果它们需要对类的成员(或对象本身,如果是save_image的引用/指针)。看起来它们成为非静态成员函数似乎更有意义。这样,他们将隐式有权访问被调用对象的成员。

您的load_image函数中还存在潜在的内存泄漏。您动态分配skip指向的数组。如果从那里到delete skip之间的任何读取操作都将引发异常,则将泄漏该数组。 256个字节可能也不足以读取整个颜色图。您可能应该使用std::vector<char>。更好的是,与其阅读不需要的值,还可以超越它们:

// Skip the ID String
file_stream.seekg(header.id_length, std::ios::cur);

// Skip the colour map if it doesn't exist
if (!(tgaDesc & 4)) {
    int colourMapSize = header.colour_map_type * header.num_entries;
    file_stream.seekg(colourMapSize, std::ios::cur);
}

编写该示例使我注意到tgaDesc始终为0,因此if块将始终运行。您是要在这里检查header.colour_map_type吗?当然,如果没有颜色映射,则header.num_entries应该为0,所以我不确定甚至不需要if

虽然我们位于load_image,但是在打开std::ios::ate时传递了file_stream标志,但随后立即seekg回到了文件的开头。如果删除std::ios::ate标志,则流最初将定位在文件的开头,多余的seekg可被删除。

读取文件头的方式通常很好。 Byte ordering (AKA endianness)可能是一个问题,但是TGA和大多数现代CPU都使用低端字节序,因此除非您想支持某些深奥的平台,否则您可能还可以。

将所有内容放在一起可以得到一个image类,看起来像这样:

class image {
public:
    TGA_HEADER header;
    std::vector<char> pixel_data;

    image(const std::string& image_path);
    ~image();

    void save_image(const std::string& file_name);
    void load_image(const std::string& path);
};

image::image(const std::string& image_path)
{
    load_image(image_path);
}

void image::load_image(const std::string& path)
{
    std::ifstream file_stream(path, std::ios::in | std::ios::binary);

    if (file_stream.is_open()) {
        int tgaDesc = 0;

        /* read header */
        file_stream.read(&header.id_length, sizeof(header.id_length));
        file_stream.read(&header.colour_map_type, sizeof(header.colour_map_type));
        file_stream.read(&header.image_type, sizeof(header.image_type));

        file_stream.read((char*)(&header.first_entry), sizeof(header.first_entry));
        file_stream.read((char*)(&header.num_entries), sizeof(header.num_entries));
        file_stream.read(&header.bits_per_entry, sizeof(header.bits_per_entry));

        file_stream.read((char*)(&header.x_origin), sizeof(header.x_origin));
        file_stream.read((char*)(&header.y_origin), sizeof(header.y_origin));
        file_stream.read((char*)(&header.width), sizeof(header.width));
        file_stream.read((char*)(&header.height), sizeof(header.height));
        file_stream.read(&header.bits_per_pixel, sizeof(header.bits_per_pixel));
        file_stream.read(&header.descriptor, sizeof(header.descriptor));

        // Skip the ID String
        file_stream.seekg(header.id_length, std::ios::cur);

        // Skip the colour map if it doesn't exist
        if (!(tgaDesc & 4)) {
            int colourMapSize = header.colour_map_type * header.num_entries;
            file_stream.seekg(colourMapSize, std::ios::cur);
        }


        int imageDataSize = header.width * header.height * (header.bits_per_pixel / 8);
        pixel_data.resize(imageDataSize);

        /* read image data */
        file_stream.read(pixel_data.data(), imageDataSize);
    }
}

答案 1 :(得分:0)

转发

几年前,我一直在关注一组3D图形视频教程。该系列的创建者是MASc的Marek A. Krzeminski,您可以在这里找到他的网站:www.marekknows.com在他的系列的下载标签下,该代码涉及2个系列,它们属于Shader Engine和Zing系列。由于多种原因,我无法显示要复制,粘贴,编译和运行的代码的所有部分。首先是关于版权的原因。 Marek以前已允许我使用他的代码来指导他人寻找自己作品的解决方案,但前提是我对他的代码表示赞赏。因此,我将要展示的代码不是我的代码,但是,我从他那里学到的概念和技术应该用于帮助解决或解决有关此问题的问题。我无法显示所有代码的另一个原因是,这是一个非常大的项目,其中集成了许多部分。我能做的就是向您展示用于加载TGA文件的功能,并说明他如何设计该文件,如何将其与项目的其他部分集成以及如何使用它。然后,我将解释它如何适用于您的情况和您的问题。


这是TextureFileReader::loadTga()函数

void TextureFileReader::loadTga( Texture* pTexture ) {
    if ( nullptr == pTexture ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " invalid pTexture passed in" ) );
    }

    struct TgaHeader {
        unsigned char   idLength;
        unsigned char   colorMapType;
        unsigned char   imageType;
        unsigned char   colorMapSpecifications[5];
        short           xOrigin;
        short           yOrigin;
        short           imageWidth;
        short           imageHeight;
        unsigned char   pixelDepth;
        unsigned char   imageDescriptor;
    } tgaHeader;

    enum TgaFileType {
        TGA_RGB     = 2,
        TGA_RLE_RGB = 10,
    }; // TgaFileType

    // Error Message Handling
    std::ostringstream strStream;
    strStream << __FUNCTION__ << " ";

    // Open File For Reading
    m_fileStream.open( m_strFilenameWithPath, std::ios_base::in | std::ios_base::binary );
    if ( !m_fileStream.is_open() ) {
        strStream << "can not open file for reading";
        throwError( strStream );
    }

    // Get TGA File Header
    if ( !m_fileStream.read( reinterpret_cast<char*>( &tgaHeader ), sizeof( tgaHeader ) ) ) {
        strStream << "error reading header";
        throwError( strStream );
    }

    // This TGA File Loader Can Only Load Uncompressed Or Compressed True-Color Images
    if ( (tgaHeader.imageType != TGA_RGB ) && (tgaHeader.imageType != TGA_RLE_RGB ) ) {
        strStream << "TGA loader only supports loading RGB{" << TGA_RGB << "} and RLE_RGB{" << TGA_RLE_RGB
            << "} encoded files. This file contains pixels encoded in an unsupported type{" << tgaHeader.imageType << "}";
        throwError( strStream );
    }

    // Convert Bits Per Pixel To Bytes Per Pixel
    unsigned uBytesPerPixel = tgaHeader.pixelDepth / 8;
    if ( (uBytesPerPixel != 3) && (uBytesPerPixel != 4) ) {
        strStream << "TGA loader only supports 24bpp or 32bpp images. This image uses " << tgaHeader.pixelDepth << " bits per pixel";
        throwError( strStream );
    }

    // Make Room For All Pixel Data
    if ( 0 == tgaHeader.imageWidth || 0 == tgaHeader.imageHeight ) {
        strStream << "invalid image size (" << tgaHeader.imageWidth << "," << tgaHeader.imageHeight << ")";
        throwError( strStream );
    }
    unsigned uTotalNumBytes = tgaHeader.imageWidth * tgaHeader.imageHeight * uBytesPerPixel;
    pTexture->vPixelData.resize( uTotalNumBytes );

    // Move Read Pointer To Beginning Of Image Data
    if ( tgaHeader.idLength > 0 ) {
        m_fileStream.ignore( tgaHeader.idLength );
    }

    // Used To Get And Flip Pixels Data
    std::vector<unsigned char> vTempPixel( uBytesPerPixel, 0 );

    if ( tgaHeader.imageType == TGA_RLE_RGB ) {
        // TGA Data Is Compressed

        // All Error Messages The Same If Error Occurs Below
        strStream << "file is corrupted, missing pixel data";

        unsigned char ucRepetitionCounter = 0;

        unsigned uTotalNumberPixels = tgaHeader.imageWidth * tgaHeader.imageHeight;
        unsigned uCurrentPixel = 0;
        while( uCurrentPixel < uTotalNumberPixels ) {
            // Get Repetition Count Value
            if ( !m_fileStream.read( reinterpret_cast<char*>( &ucRepetitionCounter ), sizeof( unsigned char ) ) ) {
                throwError( strStream );
            }

            if ( ucRepetitionCounter < 128 ) {
                // Raw Packet. Counter Indicates How Many Different Pixels Need To Be Read
                ++ucRepetitionCounter;

                // Get Pixel Values
                if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[uCurrentPixel * uBytesPerPixel] ), uBytesPerPixel * ucRepetitionCounter ) ) {
                    throwError( strStream );
                }
            } else {
                // Run-Length Packet. Counter Indicates How Many Times The Text Pixel Needs To Repeat
                ucRepetitionCounter -= 127;

                // Get Pixel Value
                if ( !m_fileStream.read( reinterpret_cast<char*>( &vTempPixel[0] ), uBytesPerPixel ) ) {
                    throwError( strStream );
                }
                // Save Pixel Multiple Times
                for ( unsigned int u = uCurrentPixel; u < ( uCurrentPixel + ucRepetitionCounter ); ++u ) {
                    memcpy( &pTexture->vPixelData[u * uBytesPerPixel], &vTempPixel[0], uBytesPerPixel );
                }
            }

            // Increment Counter
            uCurrentPixel += ucRepetitionCounter;
        }
    } else {
        // TGA Data Is Uncompressed
        // Get Pixel Data
        if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[0] ), pTexture->vPixelData.size() ) ) {
            strStream << "file is corrupted, missing pixel data";
            throwError( strStream );
        }
    }
    m_fileStream.close();

    // Convert All Pixel Data from BGR To RGB
    unsigned char ucTemp;
    for ( unsigned int u = 0; u < uTotalNumBytes; u += uBytesPerPixel ) {
        ucTemp                      = pTexture->vPixelData[u];      // Save Blue Color
        pTexture->vPixelData[u]     = pTexture->vPixelData[u + 2];  // Set Red Color
        pTexture->vPixelData[u + 2] = ucTemp;                       // Set Blue Color
    }

    // Flip Image Horizontally
    if ( tgaHeader.imageDescriptor & 0x10 ) {
        short sHalfWidth = tgaHeader.imageWidth >> 1;
        for ( short h = 0; h < tgaHeader.imageHeight; ++h ) {
            for ( short w = 0; w < sHalfWidth; ++w ) {
                unsigned uPixelLeft  = uBytesPerPixel * ( h * tgaHeader.imageWidth + w );
                unsigned uPixelRight = uBytesPerPixel * ( h * tgaHeader.imageWidth + tgaHeader.imageWidth - 1 - w );

                memcpy( &vTempPixel[0],                     &pTexture->vPixelData[uPixelLeft],  uBytesPerPixel ); // Store Left Pixel
                memcpy( &pTexture->vPixelData[uPixelLeft],  &pTexture->vPixelData[uPixelRight], uBytesPerPixel ); // Save Right Pixel @ Left
                memcpy( &pTexture->vPixelData[uPixelRight], &vTempPixel[0],                     uBytesPerPixel ); // Save Left Pixel @ Right
            }
        }
    }

    // Flip Vertically
    if ( tgaHeader.imageDescriptor & 0x20 ) {
        short sHalfHeight = tgaHeader.imageHeight >> 1;
        for ( short w = 0; w < tgaHeader.imageWidth; ++w ) {
            for ( short h = 0; h < sHalfHeight; ++h ) {
                unsigned uPixelTop    = uBytesPerPixel * ( w + tgaHeader.imageWidth * h );
                unsigned uPixelBottom = uBytesPerPixel * ( w + tgaHeader.imageWidth * ( tgaHeader.imageHeight - 1 - h ) );

                memcpy( &vTempPixel[0],                      &pTexture->vPixelData[uPixelTop],    uBytesPerPixel ); // Store Top Pixel
                memcpy( &pTexture->vPixelData[uPixelTop],    &pTexture->vPixelData[uPixelBottom], uBytesPerPixel ); // Save Bottom Pixel @ Top
                memcpy( &pTexture->vPixelData[uPixelBottom], &vTempPixel[0],                      uBytesPerPixel ); // Save Top Pixel @ Bottom
            }
        }
    }

    // Store Other Values In Texture
    pTexture->uWidth          = tgaHeader.imageWidth;
    pTexture->uHeight         = tgaHeader.imageHeight;
    pTexture->hasAlphaChannel = ( tgaHeader.pixelDepth == 32 );

}

该函数由TextureFileReader::getOrCreateTextureInfo()

调用
TextureInfo TextureFileReader::getOrCreateTextureInfo( TextureInfo::FilterQuality filterQuality, bool generateMipMap, bool wrapRepeat ) {
    TextureInfo textureInfo = m_pAssetStorage->getTextureInfo( m_strFilenameWithPath );
    if ( INVALID_UNSIGNED == textureInfo.uTextureId ) {
        // Need To Create The Texture
        Texture texture( filterQuality, generateMipMap, wrapRepeat );

        if ( !loadPng( &texture ) ) {
            loadTga( &texture );
        }
        textureInfo = m_pAssetStorage->add( texture, m_strFilenameWithPath );
    }

    return textureInfo;
} 

TextureFileReader::loadTga()使用指向Texture对象的指针。该纹理对象不过是AssetsStorage类的头文件中声明的结构。我不会在这里显示AssetsStorage类,但是会显示Texture声明:

struct Texture {
    bool        hasAlphaChannel;
    bool        generateMipMap;
    bool        wrapRepeat;
    unsigned    uWidth;
    unsigned    uHeight;
    TextureInfo::FilterQuality filterQuality;
    std::vector<unsigned char> vPixelData;

    Texture( TextureInfo::FilterQuality filterQualityIn, bool generateMipMapIn, bool wrapRepeatIn ) :
        hasAlphaChannel( false ),
        generateMipMap( generateMipMapIn ),
        wrapRepeat( wrapRepeatIn ),
        uWidth( 0 ),
        uHeight( 0 ),
        filterQuality( filterQualityIn )
    {}
}; // Texture

在调用TextureFileReader::loadTGA()TextureFileReader::loadPNG()之前,此函数将生成一个TextureInfo对象。这是在“ CommonStructs.h”中声明的另一个结构,可以在这里看到:

struct TextureInfo {
    enum FilterQuality {
        FILTER_NONE = 1,
        FILTER_GOOD,
        FILTER_BETTER,
        FILTER_BEST
    }; // FilterQuality

    unsigned    uTextureId;
    bool        hasTransparency;
    glm::uvec2  size;

    TextureInfo() :
        uTextureId( INVALID_UNSIGNED ),
        hasTransparency( false ),
        size( glm::uvec2( 0, 0 ) )
    {}
};

如果通过查找其ID作为其ID的文件名已经存在,它将尝试从TextureInfo中检索AssetStorage对象,如果存在则返回它。如果在textureInfo中找不到AssetStorage,则需要创建一个Texture和一个TextureInfo对象。


如果不存在texture,并且需要创建一个TextureFileReader::getOrCreateTextureInfo()方法,则会创建TextureInfoTexture对象的本地堆栈对象。 TextureInfoTexture对象的构造都没有动态内存。这些对象的内存由AssetStorage类处理。如果Texture对象的构建成功,则此函数将尝试调用loadPng()loadTga(),因为此引擎支持两种图像文件格式。这是我们将地址传递到临时Texture对象的地方。

如果在没有引发任何异常的情况下成功打开,读取和解析文件,并且所有图像数据均有效,则控制流将离开load方法的范围并返回到{ {1}}成员函数。然后,这将尝试将getOrCreateTextureInfo对象从其texture成员函数存储到AssetStorage类的成员容器中(如果尚不存在)。无论哪种方式,此成员函数都将返回add(/*resource*/)类的textureInfo引用的AssetStorage对象。


这就是add(/*resource*/)TextureFileReader用于创建AssetStorage ...

中使用的Texture对象的方式。

Engine的构造函数中的game.cpp文件或application.cpp文件中,这里是正在创建的纹理的实例:

Game

TextureFileReader titleTextureFileReader( "Assets/images/titleScreen.png" ); m_titleTextureInfo = titleTextureFileReader.getOrCreateTextureInfo( TextureInfo::FILTER_NONE, false, false ); 类正在自动使用AssetStorage。有一个指向TextureFileReader对象的指针,其中AssetStorage类是AssetStorage对象。无法直接在singleton的标头中看到成员变量,因为这是从TextureFileReader基类派生的类。此引擎中有大量的继承和多态行为!


现在就您的问题进行全面介绍,如果您通读FileHandler函数并查看如何使用loadTga(),则该结构仅存在于此TgaHeader函数。读取文件并存储数据后,我们将不再需要它。数据存储后,我们将对其进行解析并将其处理为我们要支持的格式。至于实际的像素数据,您可以看到它们清楚地存储在声明为loadTga()容器的pTexture的{​​{1}}成员中。这里没有地方使用vPixelDatastd::vector<unsigned char> ...

现在,对于std::shared_ptr<T>std::unique_ptr<T>对象的内存管理,这些由Texture类处理...

TextureInfo类具有AssetStorage的{​​{1}}支持...

AssetStorage

从这个typedef开始,它具有以下成员变量:

Texture

此类还提供了几种处理纹理的方法:

typedef std::unordered_map<std::string, TextureInfo>                    MapTextureInfos;

在这里,我将显示与typedef相关的MapTextureInfos m_textureInfos; 方法,此函数的版本可以添加TextureInfo getTextureInfo( const std::string& strFilename ) const; TextureInfo add( const Texture& texture, const std::string& strFilename ); bool removeTextureInfo( const std::string& strFilename ); bool removeTextureInfo( unsigned uTextureId ); void showLoadedTextureInfo() const; AssetStorage::add(/*resource*/)Textures,{ {1}}等...

Audio

对于属于Fonts类(属于Sprites的基类)的2D & 3D Rednerables类的实例,它声明为:

TextureInfo AssetStorage::add( const Texture& texture, const std::string& strFilename ) {
    if ( INVALID_UNSIGNED != getTextureInfo( strFilename ).uTextureId ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " can not store " << strFilename << " multiple times";
        throw ExceptionHandler( strStream );
    }

    TextureInfo textureInfo;
    textureInfo.hasTransparency = texture.hasAlphaChannel;
    textureInfo.size = glm::uvec2( texture.uWidth, texture.uHeight );

    glGetError(); // Clear Errors

    glGenTextures( 1, &textureInfo.uTextureId );

    GLenum err = glGetError();
    if ( err != GL_NO_ERROR ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed glGenTextures with error code 0x" << std::hex << err;
        throw ExceptionHandler( strStream );
    }

    glBindTexture( GL_TEXTURE_2D, textureInfo.uTextureId );

    // Wrap Textures
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );

    const glm::uvec2& openglVersion = s_pSettings->getOpenglVersion();

    if ( texture.generateMipMap ) {
        switch ( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE : {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
                break;
            }
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
                break;
            }
            case TextureInfo::FILTER_BEST: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
            }
        } // switch

        if ( openglVersion.x < 3 ) {
            // In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
            // So For Those Versions We Use glGenerateMipmap Below
            static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
            glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
        }
    } else {
        // No MipMaps
        switch ( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE:
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            }
        } // switch
    }

    // Load Texture Into Video Memory
    glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
    glTexImage2D( GL_TEXTURE_2D,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
        texture.uWidth,
        texture.uHeight,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
        GL_UNSIGNED_BYTE,
        &texture.vPixelData[0] );

    if ( texture.generateMipMap && openglVersion.x >= 3 ) {
        glGenerateMipmaps( GL_TEXTURE_2D );
    }

    // Store textureId
    BlockThread blockThread( s_criticalSection );
    m_textureInfos.insert( MapTextureInfos::value_type( strFilename, textureInfo ) );

    if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        Logger::log( std::string( "Created " ) + strFilename );
    }

    return textureInfo;
} // add

,并由AssetStorage的构造函数初始化为:

FileHandler

如您所见,此TextureFileReader具有许多集成的部分,并且没有使用“手动”动态内存,也没有在此上下文中直接用于图像文件的任何智能指针。所有这些都通过使用的容器的动态内存来处理。现在,此引擎的其他部分中,有些对象是static AssetStorage* m_pAssetStorage; ,而另一些是FileHandler对象,这取决于对象的生存期和对象的所有权。在这种情况下看不到使用if ( bSaveExceptionInLog && nullptr == m_pAssetStorage ) { m_pAssetStorage = AssetStorage::get(); } Engine的原因是由于整体引擎的设计方式。在这里,shared_ptr中的unique_ptr类使用的shared_ptr对象是unique_ptr类的成员变量的引用对象:

AssetStorage

Game类驻留在库中,而TextureFileReader类驻留在主应用程序项目中。 Engine类是从std::unique_ptr<AssetStorage> m_pAssetStorage; 类继承的。

这里Engine负责整个生命周期,并拥有指向所有单例对象(例如GameGameEngine,{ {1}}和Engine。所有这些对象都是在ShaderManager的构造函数中创建的,该构造函数在调用AudioManager类或从其继承的BatchManager类时被调用。它们的生命周期都与FontManager类的生命周期相同,或者直到它们被明确释放为止。现在,对于shared_ptr对象,它们将在AnimationManagerEngineGameApplication等中找到,因为它们是可以由多个对象使用的共享资源。


这不仅可以直接回答有关Engine的使用或编写纹理或图像加载器的实现的问题,而且还可以说明这种最低限度的复杂性和健壮但灵活,动态且通用的集成系统。

如果您想了解更多有关此代码的信息,那么在此软件的工程设计中将使用它的设计结构以及规划和设计方法,我强烈建议您访问Marek的网站。尽管他的内容不是免费的,但也不是真正昂贵的。我想说,多年来我从购买和查看他的内容中学到的知识非常值得。我并不是说他的方法,技术或实现设计是最好的,但他对自己的工作方式和原因的解释和说明是您收藏中的宝贵资源和工具。我会说这是一项很好的投资。