Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++ without pointers

Started by TimSarbin Jan 1, 2016 at 3:41 AM 11 replies 6.8k views
Original Post
TimSarbin
TimSarbin

I get C and pointers very well. But I'm trying to use C++ ~without~ standard pointers. It's working for the most part, but now I'm running into an issue:


// MyInterface &something = MyDummyBase(); is defined in the header as a private member


void MyClass::SetSomething(MyInterface &newSomething) {
   something = newSomething;
}

MyInterface &MyClass::GetSomething() {
  return something;
}

Now here's where it gets weird, from the caller:

1. myClassObj.SetSomething(AnotherClass());

2. MyInterface &Value = myClassObj.GetSomething();

At line 1, it calls the constructor of AnotherClass (as expected), sets something to AnotherClass (again, as expected), then calls the destructor of AnotherClass (not expected)

At line 2, it returns MyDummyBase instead. There are no lines inbetween.

All I can think, is that it considers AnotherClass() out of scope now, and destroys it. But what confuses me is, how does it know to revert back to MyDummyBase? I'm trying to do the equiv of .SetSomething(new AnotherClass()), but without raw pointers.

I'm assuming I have to explicitly declare AnotherClass outside of the method, and use it directly in the SetSomething method, but what happened with this logic confounds me.

Any ideas?

[Edit] Nevermind... it's not a pointer, so it can only be set in the constructor. Dur...

fastcall22
fastcall22
You cannot get very far without using just pointers in C++. Eventually you'll run into object lifetime issues. See C++ Faq Lite on references.

In summary:

1. A reference cannot be re-seated.
2. An object must be stored somewhere, and you'll run into object slicing.
3. If you want the benefits of references without the "danger" of pointer (not implying that references are inherently safe), then look into using smart pointers instead.


inb4 Servant of the Lord's in depth explanation!
Khatharr
Khatharr

A) What did you expect to happen in case 1? Why did you expect the destructor to not be called?

B) For case 2 you've invoked assign/copy on the original object. How is that defined (explicit/implicit)?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Servant of the Lord
Servant of the Lord

All I can think, is that it considers AnotherClass() out of scope now, and destroys it.

Correct.

MyInterface &something = MyDummyBase(); is defined in the header as a private member

This is also out of scope, gets destroyed, and points at invalid memory. You should've got a compile error, methinks, or at least a warning.

But what confuses me is, how does it know to revert back to MyDummyBase?

It doesn't. It's pointing at invalid memory, and just happens to be acting as if it's pointing at a MyDummyBase.

I'm trying to do the equiv of .SetSomething(new AnotherClass()), but without raw pointers.


Are you aware of smart pointers?

Anyway, what you need to do is:


myClass.SetSomething(object_that_is_guaranteed_to_exist_longer_than_myClass);

'myClass' already exists somewhere. Whereever 'myClass' exists, make sure your interface class is created before it and is destroyed after it.

For example:


int main()
{
    InterfaceNya interface;
    MyClass myClass;
    myClass.SetSomething(&interface);

    //...bunch of code in your program.

    return 0; //'myClass' gets destroyed first, then 'interface'.
}

Are you aware that references are pointers, just with different syntax?
Are you aware that you can call 'new' and give the memory to a reference?
Are you aware that raw pointers aren't bad, and are actively used is clean, modern, C++? (We just don't use raw pointers for *owning* memory)

If this is just for fun and practice, great! But if you think this is the preferred way to program, then you are slightly off-base. smile.png

TimSarbin
TimSarbin

Yeah, I've done a lot of C so malloc/realloc/free is more natural to me than new/delete, and other C++y things. I also use C# a lot, which has GC, which is a very different beast. C++ feels like a weird middle of the road, not quiet C#, not quite C. I do realize references are pointers, as the only things that actually exist are 64 bit values (or smaller depending on the architecture). You can't actually represent "MyClass" as anything more than a pointer to a block of memory that contains atomic values (int/byte/etc) or pointers (struct/class/etc) as far as passing goes -- it just pushes a pointer to the object onto the stack before calling the method.

So I'll read up on smart pointers, but honestly at this point I might as well just stick with new/delete as it is closer to malloc/free that I'm used to.

Thanks guys!

[Edit]

Yup I switch to standard pointers where it makes sense. I'll use visual leak detector and keep an eye out on things!

WoopsASword
WoopsASword

I get C and pointers very well. But I'm trying to use C++ ~without~ standard pointers. It's working for the most part, but now I'm running into an issue:


// MyInterface &something = MyDummyBase(); is defined in the header as a private member


void MyClass::SetSomething(MyInterface &newSomething) {
   something = newSomething;
}

MyInterface &MyClass::GetSomething() {
  return something;
}

Now here's where it gets weird, from the caller:

1. myClassObj.SetSomething(AnotherClass());

2. MyInterface &Value = myClassObj.GetSomething();

At line 1, it calls the constructor of AnotherClass (as expected), sets something to AnotherClass (again, as expected), then calls the destructor of AnotherClass (not expected)

At line 2, it returns MyDummyBase instead. There are no lines inbetween.

All I can think, is that it considers AnotherClass() out of scope now, and destroys it. But what confuses me is, how does it know to revert back to MyDummyBase? I'm trying to do the equiv of .SetSomething(new AnotherClass()), but without raw pointers.

I'm assuming I have to explicitly declare AnotherClass outside of the method, and use it directly in the SetSomething method, but what happened with this logic confounds me.

Any ideas?

[Edit] Nevermind... it's not a pointer, so it can only be set in the constructor. Dur...

1- Out of scope.

2- Inheritence.

You're using it wrong.

A) Nothing is wrong with pointers.

B) A better solution is using std::unique_ptr and std::shared_ptr rather than implementating everything with references (which isn't ideal for certian situations).

It does the memory managment you need, not like C# GC but it does deallocates the memory (No need for manual deletion).

C) Use them when an instance owns memory, but when you need to refernece , use a regular pointer. (The instance does not own the memory).

kbw
kbw
You don't need a pointers in C++ these days. But the biggest problem is the declaration of 'something;. It shouldn't be a reference, it should be value as you intend to rebind it with SetSomething. So remove the reference, and your code will work as expected.
Satharis
Satharis

I also use C# a lot, which has GC, which is a very different beast.

If anything they're rather similar, in languages like C# and Java everything is basically a pointer(or acts just like one) maybe I should say pointer with training wheels because the GC handles de-allocation of memory so you can essentially let things go out of scope on a whim. You can get similar behavior in C++ by using smart pointers, but obviously you have to take care of what you're doing.

So I'll read up on smart pointers, but honestly at this point I might as well just stick with new/delete as it is closer to malloc/free that I'm used to.

You're never going to take advantage of the features of a language by just trying to treat it like another language. That'd be like being used to drifting so when you switch to driving a bus you try to drift everywhere. Not super productive.

Smart pointers are a relatively new feature(at least in the standard library) or at least the good ones we have now are. But they're pretty simple to use, unique_ptr can be just thought of as changing the lifetime of a pointer to be just like a stack variable, as soon as it goes out of scope the memory gets freed with it. They're mainly important in C++(besides being nice to use) because C++ has a lot of corner cases with exceptions where things can be left dangling without careful planning. Placing all that load on the destructor means(in every case I can think of anyway) the memory gets freed even if everything comes crashing down.
xeyedmary
xeyedmary

Sounds like you're avoiding new and delete for some reason I don't quite understand. New and delete are just malloc and free wrapped under the covers. If Malloc and free may seem more natural to you, keep using them, you're going to have a difficult time avoiding heap allocation altogether. Or switch to java, you can new objects all over the place and not worry about deleting them.

SmkViper
SmkViper

Sounds like you're avoiding new and delete for some reason I don't quite understand. New and delete are just malloc and free wrapped under the covers. If Malloc and free may seem more natural to you, keep using them, you're going to have a difficult time avoiding heap allocation altogether. Or switch to java, you can new objects all over the place and not worry about deleting them.


Do not use malloc and free on C++ objects, as the constructors and destructors will not be called.

Technically you can malloc the memory and then placement-new the object into the memory - assuming correct alignment - and then manually call the destructor before calling free - but why go to all that trouble when new and delete do it correctly with less typing? There are valid use cases for placement-new and manual destructor calls, but most people won't run into them in normal coding.
TimSarbin
TimSarbin

New question as I'm trying to use the new pointer features...

if I have in my class:


std::shared_ptr<Scene> CurrentScene;
std::shared_ptr<Scene> NextScene;

and a function like so:


void SetNextScene(std::shared_ptr<Scene> nextScene) {
   NextScene = nextScene;
}
 
void Update() {
   if (NextScene != null) {
      CurrentScene->Finalize();
      NextScene = CurrentScene;
      CurrentScene = NULL;
   }
}

I set the scene like this:


sceneManager->SetNextScene(SceneMainMenu::Create(settings));

Is this a proper way to 'flip' pointers?

Servant of the Lord
Servant of the Lord

Almost.


void SetNextScene(std::shared_ptr<Scene> nextScene) {
   NextScene = nextScene;
}
 
void Update() {
   if (NextScene) { //Was "if (NextScene != null)"
      CurrentScene->Finalize();
      NextScene = CurrentScene; //Don't you mean 'CurrentScene = NextScene'?
      //CurrentScene = NULL; //Not needed here (I'll explain below).
   }
}
'null' isn't a keyword in C++. You have NULL (which you shouldn't use anymore), and nullptr (which you should use). But since smart pointers are classes that wrap pointers internally, you just check them like if(smartPtr) or if(!smartPtr).
To 'erase' a shared pointer, you could do CurrentScene.reset(nullptr), or just CurrentScene.reset().
But in this case, you could just do nothing, since as you're assigning NextScene to CurrentScene, it'll basically go like this:

if (NextScene) {                //NextScene = TheNewScene
      CurrentScene->Finalize(); //CurrentScene = TheOldScene
      CurrentScene = NextScene; //Now both point at TheNewScene, and TheOldScene gets automatically deleted so no shared_ptrs are pointing at it anymore.
   }
If you want to swap the two pointers, instead of overwriting them, you can do this:

CurrentScene.swap(NextScene);
xeyedmary
xeyedmary

Sounds like you're avoiding new and delete for some reason I don't quite understand. New and delete are just malloc and free wrapped under the covers. If Malloc and free may seem more natural to you, keep using them, you're going to have a difficult time avoiding heap allocation altogether. Or switch to java, you can new objects all over the place and not worry about deleting them.


Do not use malloc and free on C++ objects, as the constructors and destructors will not be called.

Technically you can malloc the memory and then placement-new the object into the memory - assuming correct alignment - and then manually call the destructor before calling free - but why go to all that trouble when new and delete do it correctly with less typing? There are valid use cases for placement-new and manual destructor calls, but most people won't run into them in normal coding.

Don't be pedantic. I wasn't saying he should use malloc with classes.

samoth
samoth

Try to abstain from such extremes as "use C++ without pointers". You don't want to use C++ without integers either, do you.

Any kind of extreme is bad. There is no single undisputably correct "always do X" rule. C++ can do without raw pointers to a great extent, and in many cases this is preferrable (it's safer, and sometimes more idiomatic). On the other hand, there is nothing inherently wrong with pointers, and in many situations they are just the right thing.

Similar is true for malloc/free vs new/delete. Generally, you want new/delete because that is the "correct" thing which properly calls constructors and destructors. Then again, you preferrably don't want to use new/delete directly in modern C++, but rather something like e.g. make_unique. Usually, but not necessarily. I'm still using new and delete all the time. Yes, some people will frown at you, but let them frown. Try to make your code reasonable, not a religion. When it's reasonable (and safe) to use a raw pointer, well, then do it.

On malloc, again, usually you don't want to use it. But... sometimes you just want a block of memory where you'll write some stuff into (or where the operating system will place some string or a bunch of structures, or whatever), and you don't care about constructors and destructors being called (since they're trivial, or since there simply is no such thing for a raw block of memory). In that case, it's mighty fine to just use malloc instead (on my compiler, GCC 6.2, this makes a difference of 50kB in executable size!). It's just not what you want to do by default. You do it when you deem it justified.

Also, please note that "I realize references are pointers" is a misconception. You should not think of them that way. Although references may in some situations be implemented using what's effectively a pointer, they are by no means pointers. A reference is an alias, i.e. the same identical variable under a different name, and it has much stricter rules than a pointer. It must be properly initialized (with a valid object) and it cannot be changed to refer to something different later. In my experience, compilers are much, much better at optimizing function calls with references than with pointers. Most of the time, calling a function that takes a reference to some local variable generates exactly the same code as if you copied the function body's code into the place where you made the call, and text-renamed the variables. Except it's much more readable and better structured, which is a really good thing. Maybe one day, it will be that way with pointers, too. But in my experience, we're not there yet.

It is a bit difficult to create a null reference (intentionally at least... unintentionally it's impossible!) or an invalid reference, and it is hard to have a dangling reference too (actually... with lambda captures that is no longer true, it's pretty easy to have dangling references with these...). So, all in all references are always "better", right? They're not, because sometimes you just need what a pointer can do, and what a reference can't do. Again, no extremes. Use what's right for the situation, not what some rule tells you.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.