为什么有些库提供无指针访问,而另一些则没有?

时间:2018-07-17 09:09:56

标签: c++ pointers

使用许多范例可以完成C ++编程。可以不使用任何指针,标准指针甚至是智能指针。用于在屏幕上显示图形的主流库处理的情况有所不同。例如,在SFML中,无需使用指针语法即可创建新窗口,而在Ogre3D中,则必须使用指针。

// SFML without pointer
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
// SFML with pointers
sf::Window* m_Window;
// Ogre3d with pointer
RenderWindow *renderWindow = root->createRenderWindow("Main",320,240,false);

始终可以使用指针,因为指针是更强大的概念。所以我的问题是:SFML可以为最终用户提供无指针API的窍门是什么?我们如何编写无需指针即可实例化的类?还是缺少指针不是一个好主意,SFML是错误的,因为它们不建议使用指针吗?

3 个答案:

答案 0 :(得分:3)

图形库在这里有什么特别之处?

这里的问题是,图书馆如何支持所有权管理。处理原始指针可能导致内存泄漏/死对象。因此,某些库提供程序会将用户引导到他们自己的内存管理中。 gtkmm有自己的智能指针,通常使用它们。

  

总是可以使用指针,...

不! 1)并非总是可能!如果库提供者保护了构造函数,并且只允许您通过创建方法来创建实例,那么您将无法获取lib对象的原始指针!

  

,...因为指针是更强大的概念。

原始指针没有比智能指针或引用更强大的功能。唯一不能直接完成的事情是使用虚拟调度。但这也可以在智能指针实现中进行管理。我认为没有理由接受您的发言;)

  

...我们如何编写无需指针即可实例化的类?

您可以实例化每个没有指针的类。

Type instanceVar{<Constructor Parms>}; 

将完成这项工作。无需使用newnew@

  

或者没有指针是个坏主意

不!如果有解决所有权的通用方法,这是一个好主意。可以在库内部容器中处理,智能指针是其他一些概念。

我个人的梦想是,我想决定如何使用图书馆。由于c ++仍提供智能指针,因此我不想使用特定于库的指针。但是许多常见的库在智能指针成为c ++的一部分之前就已经开始开发。因此,他们仍然带有自己的实现和所有权管理。这是我不喜欢的东西,但这是我非常个人的观点,并且基于这种观点!

答案 1 :(得分:1)

这取决于特定API打算执行的操作:

// SFML without pointer
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");

在上述情况下,库将调用构造函数,因此它不能是指针。

// Ogre3d with pointer
RenderWindow *renderWindow = root->createRenderWindow("Main",320,240,false);

对于ogre3d,它们的实现方式不同。这是设计模式的决定。他们可能在幕后使用Factory设计模式。

答案 2 :(得分:0)

总体思路是:

您的变量必须保留在范围内。如果在函数中创建变量,则函数结束时将停止存在。仅当您将变量而不是堆放在堆栈上时,这才是正确的。所有变量都在堆栈上,唯一的例外是使用新的,静态的或全局变量创建的变量。因此,如果您不使用new和指针,则变量的内容将丢失。示例:

char* readName()
{
    char name[64];
    scanf("%s", name);
    return name;
}

int main()
{
    char* name = readName();
    std::cout << name << std::endl;
}

您将获得:

g++ po.c -o po
po.c: In function ‘char* readName()’:
po.c:7:10: warning: address of local variable ‘name’ returned [-Wreturn-local-addr]
     char name[64];
          ^
./po
Input: xxx
Output: @`

这不是您想要的,您将获得随机数据,因为变量已停止存在,因为它在堆栈中。让我们对堆上的变量进行处理,这样就不会删除它。

#include <iostream>
#include <stdio.h>
using namespace std;

char* readName()
{
    char* name = new char(64);
    scanf("%s", name);
    return name;
}

int main()
{
    char* name = readName();
    std::cout << name << std::endl;
    delete[] name; //you have to delete heap variables to get the RAM back
}

您看到该名称在堆上创建。让我们使用它。

g++ po.c -o po
./po
Input: xxx
Output: xxx

请等待两次,您都会抱怨。的确如此,因为我想通过数组向您展示。如果图形库使用的是指向视频内存的指针,则视频内存也是“堆上”的数组。免责声明:不是真正的堆,而是gpu,但它的作用类似于堆上的数组。但是请不要免费使用它!

另一个原因是所需的RAM数量。

struct Level
{
 size_t data[1*1024*1024*1024]; //1 gigabyte data
};

Level* getLevelPointer()
{
    Level level = new Level();
    return level;
}

Level getLevelCopy()
{
    Level level;
    return level;
}

void main()
{
   Level* level = getLevelPointer();
   Level level2 = getLevelCopy();
}

如您所见,指针必须复制1 GB而不是4到8字节。而且,如果难以复制某些内容,有时只提供一个指针会更容易。

您可以通过使用包装器类来避免此问题。

struct Level
{
 size_t data[1*1024*1024*1024]; //1 gigabyte data
};

class LevelWrapper
{
  Level level;
  LevelWrapper() : level()
  {

  }
  inline Level& getLevel()
  {
    return level;
  }
};

Level* getLevelPointer()
{
    Level level = new Level();
    return level;
}

Level getLevelCopy()
{
    Level level;
    return level;
}

void main()
{
   Level* level = getLevelPointer(); //level lives on the heap
   Level level2 = getLevelCopy(); //level is created on copied to main 
   LevelWrapper wrapper;
   Level& level3 = wrapper.level(); //level lives in wrapper, using a reference to get it
}

如果您使用指针,那么您也不会对深层副本感到困惑。如果使用指针,就知道您共享数据。如果使用该类的变量副本,则认为您拥有数据,并且可以在不更改其他变量的情况下对其进行更改。这是一个例子。如果更改p2的名称,即使它们是副本而不是指针,我也会更改p1的名称。输出是Person内部指针的地址(抱歉,C风格的编码)。

#include <stdio.h>
#include <malloc.h>

struct Person
{
   int age;
   char* name;
};


void main()
{
        struct Person p1;
        p1.name = (char*) malloc(64*sizeof(char));
        p1.name = "Junior";
        p1.age = 16;
        struct Person p2 = p1;

        printf("%p %p \n", p1.name, p2.name);
}

./pers 
0x400624 0x400624 

顺便说一句。使用共享指针而不是常规指针。它们有时会慢一点,但是您不必使用delete清理它们(有时您可能会忘记)。

std::shared_ptr<MyObject> MyObjectPtr(new MyObject("Strawberries"));