class 成员使用 unique_ptr

Use of unique_ptr for class members

我正在实现一个 C++ 程序来从 TGA 文件中读取图像。我得到了 header.

的结构
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 看起来像这样:

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);
}; 

我的图像的构造函数class:

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

还有我的加载class:

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);
}

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

我是 C++ 的新手,所以请随时评论您可能遇到的其他问题。

此致

从技术上讲,您在这里使用 std::unique_ptr 没有任何问题,但在这种情况下完全没有必要。 std::vector 本质上已经是指向底层数组的唯一指针。不要让你的 class 有一个 std::unique_ptr<std::vector<char>> 成员,它应该只有一个 std::vector<char> 成员。


我还会质疑为什么 load_imagesave_image 是静态的,如果它们需要 references/pointers 到 class 的成员(或者对象本身,在这种情况下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 时,您在打开 file_stream 时传递 std::ios::ate 标志,但随后立即 seekg 回到文件的开头。如果删除 std::ios::ate 标志,则流最初将位于文件的开头,并且可以消除额外的 seekg

您读取文件头的方式基本没问题。 Byte ordering (AKA endianness) 可能是个问题,但 TGA 和大多数现代 CPU 都使用小端字节顺序,所以除非你想支持一些深奥的平台,否则你可能没问题。

把它们放在一起会给你一个 image class 看起来像这样的东西:

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);
    }
}

转发

几年前,我正在关注一组 3D 图形视频教程。该系列的创建者是 Marek A. Krzeminski,MASc,您可以在此处找到他的网站:www.marekknows.com 在他的下载选项卡下的系列中,此代码引用了 2 个系列,它们属于着色器引擎和振系列。由于多种原因,我无法显示代码的所有部分以进行复制、粘贴、编译和 运行。首先是版权原因。 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 object 的指针。这个纹理 object 只不过是在 AssetsStorage class 的 header 文件中声明的结构。我不会在这里显示 AssetsStorage class,但我会显示 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()之前,该函数会生成一个TextureInfoObject。这是在 "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 ) )
    {}
};

它将尝试从 AssetStorage 中检索一个 TextureInfo object 如果一个已经存在通过查找它的文件名作为它的 id 如果是这样它将 return它。如果在AssetStorage中找不到textureInfo,则需要创建一个Texture和一个TextureInfoobject.


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

如果文件的打开、读取和解析成功且没有抛出任何异常并且所有图像数据都有效,控制流将离开 load 方法和 return 回到 getOrCreateTextureInfo 成员函数。然后这将尝试将 texture object 从其 add(/*resource*/) 成员函数存储到 AssetStorage class 的成员容器中(如果尚不存在的话)。无论哪种方式,此成员函数都会 return 被 AssetStorage class 的 add(/*resource*/).[=126= 引用的 textureInfo object ]


这就是 TextureFileReaderAssetStorage 用于创建在 Engine 中使用的 Texture object 的方式...

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

TextureFileReader titleTextureFileReader( "Assets/images/titleScreen.png" );
m_titleTextureInfo =  titleTextureFileReader.getOrCreateTextureInfo( TextureInfo::FILTER_NONE, false, false );

AssetStorageTextureFileReader class 自动使用。有一个指向 AssetStorage object 的指针,其中 AssetStorage class 是 singleton object。成员变量不能直接在 TextureFileReader 的 header 中看到,因为这是从 FileHandler 基础 class 派生的 class。这个引擎中有大量的继承和多态行为!


现在就你的问题提出这个完整的循环,如果你通读 loadTga() 函数并查看 TgaHeader 是如何使用的,这个结构只存在于此 loadTga() 函数的范围。一旦文件被读取,数据被存储,我们就不再需要它了。存储数据后,我们将对其进行解析并将其操作为我们想要支持的格式。至于实际的像素数据,您可以看到它们清楚地存储在 pTexturevPixelData 成员中,该成员被声明为 std::vector<unsigned char> 容器。这里没有使用std::shared_ptr<T>std::unique_ptr<T>的地方...

现在至于TextureTextureInfoobject的内存管理,这些由AssetStorageclass...

AssetStorage class 有 typedef Texture 支持...

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

从这个typedef它有以下成员变量:

MapTextureInfos m_textureInfos;

这个class也有几种处理纹理的方法:

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;

这里我将展示属于TexturesAssetStorage::add(/*resource*/)方法,这个函数有添加AudioFontsSprites的版本, 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

至于这个实例AssetStorageclass 是 FileHandler class 的成员,它是 class 到 TextureFileReader 的基,声明为:

static AssetStorage* m_pAssetStorage;

并由 FileHandler 的构造函数初始化:

if ( bSaveExceptionInLog && nullptr == m_pAssetStorage ) {
    m_pAssetStorage = AssetStorage::get();
}

如您所见,Engine 包含许多集成部分,没有使用 "manual" 动态内存,也没有直接在图像文件上下文中使用任何智能指针。所有这些都由所用容器的动态内存处理。现在,这个引擎还有其他部分,其中一些 object 是 shared_ptr,其他部分是 unique_ptr object,这取决于 object 的生命周期以及 object 的所有权。在此上下文中看不到任何 shared_ptrunique_ptr 的使用是由于整个引擎的设计方式所致。这里,Game class 在 TextureFileReader 中使用的 AssetStorage object 是对 Engine 的引用 object的class的成员变量:

std::unique_ptr<AssetStorage>  m_pAssetStorage;

Engine class 驻留在库中,Game class 驻留在主应用程序项目中。 Gameclass继承自Engineclass。

此处 Engine 负责生命周期并拥有指向所有单例 object 的所有唯一指针,例如 ShaderManagerAudioManagerBatchManagerFontManagerAnimationManager。所有这些 object 都是在 Engine 的构造函数中创建的,当 Game class 或 Application class 继承自它被称为。它们的生命周期都与 Engine class 一样,或者直到它们被明确释放。现在至于 shared_ptr objects 这些将在 AudioManagerShaderMangerAnimationManagerFontManger 等中找到,因为它们将被共享可供多个object使用的资源。


这不仅仅是为了直接回答这个关于 unique_ptr 的使用或编写纹理或图像加载器的实现的问题,而且还作为对这种最小化的思想和设计过程的说明复杂而健壮,但又灵活、动态和通用集成的系统。

如果您想了解更多关于此代码的信息,它的设计结构以及在该软件的工程中使用的规划和设计方法,我强烈建议您访问 Marek 的网站。虽然他的内容不是免费的,但也不是很贵。多年来我从购买和查看他的内容中获得的知识我会说是非常值得的。我并不是说他的方法、技术或实现设计是最好的,但他对他如何以及为什么做他所做的事情的解释和演示是您 collection 中的重要资源和工具。我会说这是一笔非常好的投资。