使用许多范例可以完成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是错误的,因为它们不建议使用指针吗?
答案 0 :(得分:3)
图形库在这里有什么特别之处?
这里的问题是,图书馆如何支持所有权管理。处理原始指针可能导致内存泄漏/死对象。因此,某些库提供程序会将用户引导到他们自己的内存管理中。 gtkmm有自己的智能指针,通常使用它们。
总是可以使用指针,...
不! 1)并非总是可能!如果库提供者保护了构造函数,并且只允许您通过创建方法来创建实例,那么您将无法获取lib对象的原始指针!
,...因为指针是更强大的概念。
原始指针没有比智能指针或引用更强大的功能。唯一不能直接完成的事情是使用虚拟调度。但这也可以在智能指针实现中进行管理。我认为没有理由接受您的发言;)
...我们如何编写无需指针即可实例化的类?
您可以实例化每个没有指针的类。
Type instanceVar{<Constructor Parms>};
将完成这项工作。无需使用new
或new@
。
或者没有指针是个坏主意
不!如果有解决所有权的通用方法,这是一个好主意。可以在库内部容器中处理,智能指针是其他一些概念。
我个人的梦想是,我想决定如何使用图书馆。由于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"));