0

Programming in C++ can be done with many paradigms. It is possible to use no pointers, a standard pointer or even a smart pointer. The mainstream libraries for displaying graphic on the screen are handling the situation different. In SFML for example, it is possible to create a new window without using the pointer syntax, while in Ogre3D it is necessary to use pointers.

// 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);

It is always possible to use pointers, because pointers are the more powerful concept. So my question is: what is the trick in SFML that they can provide a pointerless API to the enduser and how can we write classes which can be instantiated without pointers? Or is the absence of pointers a bad idea, and SFML is wrong because they don't recommend the usage of pointers?

Manuel Rodriguez
  • 734
  • 4
  • 18
  • 1
    A smart pointer is just a class that happens to manage a resource (a heap allocated object). A sf::RenderWindow is just a class that happens to manage a resource (a window in this case). There isn't much difference between these cases. – n. m. could be an AI Jul 17 '18 at 09:38
  • 1
    Perhaps you also want to compare iostreams with stdio, and ask why iostreams work with stream objects while stdio works with FILE pointers. – n. m. could be an AI Jul 17 '18 at 09:40

3 Answers3

3

What is special here to graphic lib?

The question here is, how a library support ownership management. Dealing with raw pointers can result in memory leaks/dead objects. So some providers of libs are directing the users to their own memory management. gtkmm has own smart pointers and it is common to use them.

It is always possible to use pointers,...

No! 1) Is it not always possible! If a library provider protect the constructor and let you create instances only via a creation method, you can't get raw pointers of objects of the lib!

, ...because pointers are the more powerful concept.

There is nothing more powerful from a raw pointer than from a smart pointer or a reference. The only thing which can not be done directly is using virtual dispatching. But this can also be managed inside smart pointer implementations. I see no reason to accept your statement ;)

... how can we write classes which can be instantiated without pointers?

You can instantiate every class without pointers. Simply

Type instanceVar{<Constructor Parms>}; 

will do the job. No need to use new or new@.

Or is the absence of pointers a bad idea

No! It is a good idea if there is a common way to address ownership. That can be handled in library internal containers, smart pointers are some other concepts.

My personal fealing is, that I want to decide how I can use a library. As c++ still provides smart pointers I don't want to use library specific ones. But a lot of common libs has started development long before smart pointers are part of c++. So they come still with their own implementations and their own ownership management. This is something which I don't like but this is my very personal point of view and as this opinion based!

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • You can always take the address of an object to get a raw pointer. You just have to be careful about not using that pointer once the object is dead, which can be hard if you don't know what controls it's lifetime – Caleth Jul 17 '18 at 09:40
  • @Caleth: If you can't get the object, because constructors are private and only a creation method is friend, you can't get the object nor the pointer, if the creation method returns you a special "handle" object. This can be a smart pointer which has now interface to access to the underlying raw pointer. If so, no chance to get a raw pointer at all. – Klaus Jul 17 '18 at 10:05
  • I agree. However, the handle is still *an object*. Depending on how you look at it, it can be (thought of as) *the object* – Caleth Jul 17 '18 at 10:11
  • @Caleth: As we talk about "raw pointers to objects" i would say No ;) That this handle reflects in some useful way the object is quite clear, but did not points to the OPs question. – Klaus Jul 17 '18 at 10:13
1

It depends on what the specific API intends to perform:

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

In the above case, the library calls a constructor, hence it can not be a pointer.

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

For ogre3d, the way they implement is different. It is a design pattern decision. It might be possible that they are using a Factory design pattern behind the scene.

ConsistentProgrammer
  • 1,294
  • 10
  • 14
  • 1
    In the very broadest sense, *any* function returning a "new object" is an instance of Factory. – Caleth Jul 17 '18 at 09:38
0

The general idea is:

You variable has to stay in scope. If you create a variable in a function it will stop to exist when then the function ends. This is only true if you put the variable on stack instead of the heap. All variables are on the stack, the only exceptions are variables which are created with new, static or global variables. So if you don't use new and a pointer the content of your variable will be lost. Example:

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

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

You will get:

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: @`

This is not what you want, you get random data because your variable stopped to exist, because it was on the stack. Lets do it with a variable on the heap, so it will not be deleted.

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

You see that name is created on the heap. Lets use it.

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

Wait you used pointer both time you will complain. This is true, because I wanted to show it to you with an array. If a graphics library is using a pointer to video memory , the video memory is an array "on the heap" too. Disclaimer: Not realy the heap but the gpu but it acts like an array on the heap. But don't use free on it!

An other reason is the amount of RAM needed.

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();
}

As you can see you have to copy 1 Gigabyte instead of 4 to 8 Bytes for the pointer. And if something is hard to copy, it is sometimes easier to just give a pointer.

You can avoid this problem by using a wrapper class.

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
}

If you are using pointers you also don't have confusion with deep copies. If you use pointers you know you share the data. If you use variable copies of the class you think you own the data and can change it without changing the other variable. Here is an example. If I change the name of p2 I will change the name of p1, even if they are copies not pointers. The output is the address of the internal pointer of Person (sorry for C style coding).

#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 

Btw. use shared pointer instead of normale pointer. They are sometimes a tiny bit slower, but you don't have to clean them up with delete (which you can sometimes forget).

std::shared_ptr<MyObject> MyObjectPtr(new MyObject("Strawberries"));
stupidstudent
  • 678
  • 4
  • 13