从SDL库的函数返回的指针



我一直在关注youtube上使用SDL的游戏引擎创建者教程。我一直在学习C++中涉及的许多东西,并对指针(原始和智能)进行了更深入的研究,还学习了堆栈和堆。在本教程中,有一个小函数可以返回一个指针。我不明白的是,这个指针是如何在整个代码执行过程中仍然可用的?这是有问题的代码。

SDL_Texture* TextureManager::LoadTexture(const char* texture)
{
SDL_Surface* tempSurface = IMG_Load(texture);
SDL_Texture* tex = SDL_CreateTextureFromSurface(Game::renderer, 
tempSurface);
SDL_FreeSurface(tempSurface);
return tex;
}

这段代码包含贯穿始终的指针信息。。。

现在,为了学习这些材料,我尝试创建一些类似的东西来观察内存如何与我创建的函数中的指针一起工作。

int* test() {
int num = 5;
return #
}
int main()
{
int* ptr = nullptr;
ptr = test();
std::cout << "Hello World!n";
int testArray[5];
int i = 1;
testArray[0] = 1;
testArray[1] = 2;
}

当调用int i时,指针信息会被破坏。为什么这与上面的功能不一样,我有什么遗漏吗?

所以,test()在这里打破了这个规则。

不幸的是,我找不到足够的研究来让我满意为什么这是一条规则,但我会发表我的怀疑。

num是一个局部变量。在堆栈上分配局部变量。函数执行完成后,局部变量(即num)就会从堆栈中"弹出"。也就是说,他们被摧毁了。

通过malloc()new关键字等方式分配的变量在堆上分配。堆在程序的执行过程中没有被销毁

在这种情况下存储在num中的地址:

int *num = new int(37);

是堆上的一个地址。即使函数完成执行,它也是完全有效的。因此,您可以在函数完成后使用它。

test函数中,num是一个局部变量。它在test()结束时被摧毁。

您需要在test中分配一个整数才能使其工作。在这种情况下,指针的所有权将转移给您:您需要删除它。

int* test() {
int* num = new int(5);
return num;
}
int main() {
int* ptr = test();
std::unique_ptr<int> int_deleter(ptr);
std::cout << "Hello World!n" << *ptr;
}

返回指针的另一个示例是返回类成员。在这种情况下,请参阅有关您是否已转让所有权的文档。例如:

class A {
public:
A() : num(new int(5)) {}
virtual ~A () { delete num; }
// `A` keeps the ownership of the pointer.
const int* GetNum() const { return num; }
private:
int* num;
}
int main() {
A a;
std::cout << *a.GetNum() << std::endl;
}

返回到SDL_CreateTextureFromSurface示例,很可能在该方法中分配了一个新变量。您需要参考有关返回指针所有权的文档(无论是您还是库删除指针)。

除了其他人的回答之外,SDL是C(而不是C++)API,因此它没有RAII,这意味着没有自动调用构造函数和析构函数的环境。

您需要调用SDL_DestroyTexture来释放纹理。否则,它将导致不同于本地变量的内存泄漏。

你可能会从概念上想到

  • SDL_CreateTextureFromSurface()(或其他纹理生成API)作为new SDL_Texture
  • SDL_DestroyTexture(texture)作为delete texture

C/C++中有4个基本存储类:

  1. 自动:本地对象——通常在堆栈上创建——一旦它们所在的作用域到达其末端,就会被销毁。作用域通常位于函数体中,因此当函数返回时,在函数中构造的所有自动对象都已被销毁;这也包括函数arguments
  2. 内部/静态:它们的寿命比大多数自动变量都长。一旦构建完成,它们就一直存在,直到达到程序的终点;当它们被自动销毁时。这些对象仅在嵌套模块(转换单元)/功能/范围中可见/可访问
  3. 全局/外部:这些对象的寿命比上面前两个类中的所有对象都长。它们是在程序启动后构建的,并在程序结束前销毁,此时所有自动和内部对象都已失效
  4. 动态/堆:它们具有灵活的寿命。它们可以在程序执行期间的任何时候构建,也可以在执行之后的任何时候销毁。它们通常需要手动处理,除非使用适当的智能指针来自动管理它们的寿命。错误的一个常见来源是没有正确处理它们

所以通常情况下,返回一个指向任何非自动对象的引用/指针是可以的。

最新更新