在一个循环中对 2 个向量进行操作时出现问题

Issue with operations on 2 vectors in one loop

本文关键字:操作 问题 向量 一个 循环      更新时间:2023-10-16

当我使用此循环时,代码无法正常工作:

for(int i = 0; i < INTRO_LOGO_COUNT; i++)
{
    char path_buffer[32]; // 32 = logo_xxx.png
    sprintf(path_buffer, "data/texture/intro/logo_%d.png", i);
    std::cout << path_buffer;
    m_vecTextures.push_back(sf::Texture());
    m_vecTextures.back().loadFromFile(path_buffer);
    m_vecSprites.push_back(sf::Sprite());
    m_vecSprites.back().setTexture(m_vecTextures[i]);
    // Center
    m_vecSprites.back().setPosition(
        SCREEN_WIDTH / 2 - m_vecSprites.back().getLocalBounds().width / 2,
        SCREEN_HEIGHT / 2 - m_vecSprites.back().getLocalBounds().height / 2);
    m_vecSprites.back().setColor(sf::Color(255, 255, 255, 0)); // Opacity : 100%
}
纹理

变得混合,只有第二个纹理正常工作。(INTRO_LOGO_COUNT定义为 2)第一个纹理具有有效大小,但图像来自第二个纹理。第二个纹理具有有效的大小和图像。但是在 1 次更改(溢出到 2 个循环)之后:

for( int i = 0; i < INTRO_LOGO_COUNT; i++ )
{
    char path_buffer[32]; // 32 = logo_xxx.png
    sprintf(path_buffer, "data/texture/intro/logo_%d.png", i);
    std::cout << path_buffer;
    m_vecTextures.push_back(sf::Texture());
    m_vecTextures.back().loadFromFile(path_buffer);
}
for( int i = 0; i < INTRO_LOGO_COUNT; i++ )
{
    m_vecSprites.push_back(sf::Sprite());
    m_vecSprites.back().setTexture(m_vecTextures[i]);
    // Center
    m_vecSprites.back().setPosition(
        SCREEN_WIDTH / 2 - m_vecSprites.back().getLocalBounds().width / 2,
        SCREEN_HEIGHT / 2 - m_vecSprites.back().getLocalBounds().height / 2);
    m_vecSprites.back().setColor(sf::Color(255, 255, 255, 0)); // Opacity : 100%
}

一切正常。问题出在哪里?我尝试了不同的方法,但效果总是相同的。谢谢!

原因是来自向量的实现。创建向量时,它会分配固定大小的内存,例如,向量的初始化大小为 A。当你将元素推入向量并且大小达到A时,矢量会自动重新分配另一个大小为2*A的连续内存块,如果它不能在同一地址重新分配大小为2*A的内存块,它将分配一个新的内存块并将旧内存中的内容复制到新内存中。

所以在你的第一个循环中:

m_vecSprites.back().setTexture(m_vecTextures[i]);

从您提供的链接中,函数"setTexture"的定义是:

void Sprite::setTexture(const Texture& texture, bool resetRect)
{
    // Recompute the texture area if requested, or if there was no valid texture & rect before
    if (resetRect || (!m_texture && (m_textureRect == sf::IntRect())))
        setTextureRect(IntRect(0, 0, texture.getSize().x, texture.getSize().y));
    // Assign the new texture
    m_texture = &texture;
}

成员变量 'm_texture' 是指针类型

functon 'setTexture' 获取对 m_vecTextures[i] 的引用,但由于向量 'm_vecTextures' 仍然会增加,如果达到初始大小,向量将调整大小并移动内存块,并且引用可能变得有效。

如果向量初始化大小为 INTRO_LOGO_COUNT,则第一个循环将正常工作。

根据从 API 收集的信息进行更新

首先,您不应该为此使用sprintfsprintf是非常不安全的,如果你不小心,你可以很容易地缓冲溢出。如果你要坚持使用C函数,至少使用snprintf。 其次,您不会自己清理字符串,也不会在任何地方放置 NULL 终止符。 这很容易导致未定义的行为。

你在C++所以使用字符串流,省去你的自私努力

我会把它写成,尽管我个人会使用移动语义,或者复制构造函数,而不是继续在"后面"编辑对象......但那只是我

其次,它被保留为参考并且您没有使用指针的事实意味着我们的指针无法更改......所以 Vector 不能使用,除非你能保证以后不会调整它的大小......如果您想使用矢量,请注意

m_vectTextures.reserve(INTRO_LOGO_COUNT); //reserves size so resize won't occur
for( int i = 0; i < INTRO_LOGO_COUNT; i++)
{
    sf::Color color(255, 255, 255, 0); // Opacity : 100%
    const char* prefix = "data/texture/intro/logo_";
    const char* suffix = ".png";
    std::stringstream ostr;
    ostr << prefix << i << suffix;
    std::string path( ostr.str() )
    std::cout << path;
    // if the above doesnt cut it, consider the same idea but using snprintf to read the 3 characters you need instead of an entire buffer
    m_vecTextures.push_back( sf::Texture() );
    sf::Texture& texture = m_vecTextures.back();
    texture.loadFromFile( path.c_str() ); //if you need a string
    m_vecSprites.push_back( sf::Sprite() );
    sf::Sprite& sprite = m_vecSprites.back();
    sprite.setTexture(texture);
    // Center
    sprite.setPosition(
        ( SCREEN_WIDTH - sprite.getLocalBounds().width ) / 2,
        ( SCREEN_HEIGHT - sprite.getLocalBounds().height ) / 2 );
    sprite.setColor( color );
    //since you still aren't sure and I don't know a better way to check
    std::cout << "sprite " << i << " is using texture of " << i
              << " which had a name of " << path << std::endl;
    //if you have a getter to check any of this now would be a good time.
}

另类

  1. 使用指针向量
  2. 使用listdequeue而不是vector