【问题标题】:Issue with operations on 2 vectors in one loop在一个循环中对 2 个向量进行操作的问题
【发布时间】:2014-04-08 20:11:27
【问题描述】:

当我使用这个循环时,代码不能正常工作:

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%
}

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

【问题讨论】:

  • 函数“setTexture”的声明是什么?
  • @DieterLücking 缓冲区有足够的空间,纹理由 SFML 加载。
  • @Matt setTexture 获取纹理指针,它来自 SFML API,在这里定义 github.com/LaurentGomila/SFML/blob/master/src/SFML/Graphics/…
  • 用户然后请相应地标记您的问题,以便人们了解您正在使用哪些库...其次请澄清您的意思是纹理混合,只有第二个纹理正常工作。不清楚你的问题是什么

标签: c++ vector sfml


【解决方案1】:

原因在于vector的实现。当一个vector被创建的时候,它会分配一个固定大小的内存,比如vector的初始化大小是A。当你把元素压入vector并且大小达到A时,vector会自动重新分配另一个连续的大小为 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'是指针类型

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

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

【讨论】:

  • 这仍然是一个非常有效的观点,值得考虑。他们真的应该在这里更清楚地说明他们做了什么,因为即使我的回答也不会完全正确......
  • 是的,在 C++ 中,将引用/指针传递给另一个函数时要非常小心。
  • 很高兴我能把答案交给你。参考生命周期是这里的关键问题。私有总是可以改变的,但无效的引用是不好的。
【解决方案2】:

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

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

您使用的是 C++,因此请使用 stringstream 并节省您的精力

我会这样写,虽然我个人会使用移动语义或复制构造函数,而不是继续在“后部”编辑对象......但这只是我

其次,它被保存为参考并且您没有使用指针这一事实意味着我们的指针不能改变......所以除非您能保证以后不会调整它的大小,否则不能使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    相关资源
    最近更新 更多