Assign auto_ptr to weak_ptr - c++

I am still exploring C++ 11. So I am pretty sure I am doing something wrong. But I just can't figure out the problem.
I have following Code:
MyClass::MyClass(const PlayerEventListener* eventListener)
{
weak_ptr<PlayerEventListener> _listener;
std::auto_ptr<PlayerEventListener> autoPtr;
autoPtr.reset(const_cast<PlayerEventListener*> (eventListener));
// I get error for this line
_listener = autoPtr;
}
I get following error: No viable overloaded '='
But following code compiles fine:
MyClass::MyClass(const PlayerEventListener* eventListener)
{
weak_ptr<PlayerEventListener> _listener;
std::shared_ptr<PlayerEventListener> sharedPtr;
sharedPtr.reset(const_cast<PlayerEventListener*> (eventListener));
// I get error for this line
_listener = sharedPtr;
}
Can somebody explain why I cannot convert a auto pointer to weak pointer?

weak_ptr is an object that holds a safe reference to an object actually held by shared_ptr, not auto_ptr. Therefore, there is no any overloaded operator= that could provide assigning auto_ptr to weak_ptr in the implementation of weak_ptr. It can be verified by compiling this example code and looking at the error
In constructor 'MyClass::MyClass(const PlayerEventListener*)':
21:14: error: no match for 'operator=' (operand types are 'std::weak_ptr<PlayerEventListener>' and 'std::auto_ptr<PlayerEventListener>')
Just to remember: According to http://www.cplusplus.com/reference/memory/auto_ptr/ :
This class template (auto_ptr) is deprecated as of C++11. unique_ptr is a new
facility with a similar functionality, but with improved security (no
fake copy assignments), added features (deleters) and support for
arrays. See unique_ptr for additional information.

Related

C++ class destructor delete member if "owner"?

I know in C++ that a pointer is just that: a pointer to a memory location, and there is no concept of "owners". But consider the following situation (not necessarily good code):
class A {
public:
A(){}
~A()
{ if(myObject!=nullptr)
delete myObject;
}
void createMember()
{myObject=new CrazyCustomClass();}
CrazyCustomClass *getMember()
{return myObject;}
private:
CrazyCustomClass *myObject=nullptr;
}
If it makes a difference, CrazyCustomClass does NOT have a copy constructor, as it makes no sense to copy it. So pretty straight forward - I have a class that, at some point after instantiation, may call new to instantiate a member of type CrazyCustomClass *
The problem is that if at some point I have a copy of class A created (which is fine - I want to be able to copy class A). When that copy is deleted, so is the object pointed to by the original class A instantiation. For example:
void StupidFunction(A *firstObject){
//This is NOT a real function, it simply illustrates the effect of a third-party library function
//create a new object that is a copy of first object
A secondObject(*firstObject);
<do whatever with second object>
//secondObject goes out of scope here and gets deleted.
}
A *firstObject=new A();
firstObject->createMember();
stupidFunction(firstObject);
CrazyCustomClass *customObject=firstObject.getMember(); //this is now an invalid pointer
In the above example, the StupidFunction is from a third-party library, the idea being that it gives a "temporary" copy of the object that you can work with without messing with the original object, which is good. Class A and CrazyCustomClass are both my code and can be changed at will. Unfortunately, when the "temporary" copy is deleted, the way I wrote my destructor causes problems.
My first thought was to use shared_ptr, something like so:
std::shared_ptr<CrazyCustomClass> sharedObject=std::make_shared<CrazyCustomClass>(new CrazyCustomClass);
...but that gave me an error when compiling:
candidate constructor (the implicit copy constructor) not viable: no
known conversion from 'CrazyCustomClass *' to 'const CrazyCustomClass'
for 1st argument; dereference the argument with *
and if I do dereference the argument with *, it gives me an error about the copy constructor of "CrazyCustomClass" being deleted, which is true - there is no sensible way to copy CrazyCustomClass.
So my question is: how can I refactor class A such that myObject gets properly deleted when firstObject goes out of scope, but not when any "temporary" copies of A get deleted?
Using a shared_ptr is in fact a solution to this problem, however the code as attempted in the original question is incorrect. There are two (at least) different ways to initialize a shared_ptr (ref: https://msdn.microsoft.com/en-us/library/hh279669.aspx). First, you can do it by using new as a constructor argument:
shared_ptr<CrazyCustomClass> myObject(new CrazyCustomClass)
Secondly, and this is the generally preferred method, you can use the make_shared function (as attempted in the original post), which takes not the new object, but the arguments to be passed to the object constructor, in this case nothing:
shared_ptr<CrazyCustomClass> myObject=make_shared<CrazyCustomClass>()
The original code simply got these two methods mixed up, thus the errors about copy constructor: it was trying to instantiate a new CrazyCustomClass object with a pointer to a CrazyCustomClass object as the constructor argument.
Once using a shared_ptr, the delete in the destructor must be removed.
Tip of the hat to #tkausl and #alterigel for pointing out the error in the comments on the question!

How do a get the address of the pointer stored in a unique_ptr?

I'm trying to use the SDL2 library with c++ and as such, some of the functions need a double pointer to an SDL_Window or SDL_Renderer. I've malloc'ed some memory for an SDL_Window and gave that to a unique pointer like so:
window = unique_ptr<SDL_Window, decltype(free) *>
reinterpret_cast<SDL_Window *>(malloc(sizeof(SDL_Window))),
free};
and I used the following site as a guide for that: http://www.codeproject.com/Articles/820931/Using-std-unique-ptr-RAII-with-malloc-and-free
So now I need to get a pointer to the pointer stored inside the unique_ptr but I'm having trouble doing so.
I've tried things like:
&window.get()
// or
&&(*window)
// or
&window
// and even
&(&(*(window.get())))
All of these resulted in strange compiler errors like an l-value being required for the unary '&' operator which is entirely understandable for the first and last cases.
Updates
I now also use a raw SDL_Window * to get the address of and to give to unique_ptr. Some of my code snippets (out of context though):
SDL_Window *window_ptr;
unique_ptr<SDL_Window> window;
window = unique_ptr<SDL_Window, decltype(SDL_DestroyWindow)> (
window_ptr,
SDL_DestroyWindow);
SDL_CreateWindowAndRenderer(500, 500, SDL_WINDOW_SHOWN, &window_ptr, &renderer_ptr);
But now, I'm running this compiler error:
/usr/include/c++/5/bits/unique_ptr.h:272:18: error: no match for ‘operator=’
(operand types are ‘std::unique_ptr<SDL_Window>::deleter_type
{aka std::default_delete<SDL_Window>}’ and ‘void (*)(void*)’)
get_deleter() = std::forward<_Ep>(__u.get_deleter());
You cannot get the address of the pointer stored inside a std::unique_ptr. If you need to call C code that returns a pointer through a double pointer, you need to pass it the address of some other pointer and then separately have the std::unique_ptr take ownership of that pointer. If the std::unique_ptr allowed you to write directly to the stored pointer, it would have no way of releasing the resource it previously held.
Often, when you get tangled up in questions like these, it's a huge clue that you're doing it wrong.
For example, why do you need a SDL_Window**? Is it, for example, because you are calling SDL_CreateWindowAndRenderer, a function that creates a window? That conflicts with the fact you've already created a window object and seemingly intend to use that.
Skimming briefly at the API, the intent seems to be that SDL_Window objects are not things that you create — they are things that the library creates.
e.g. what you really want to do is something lke
SDL_Window *window_;
SDL_Renderer *renderer_;
SDL_CreateWindowAndRenderer (/* ... */, &window_, &renderer_);
unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)>
window(window_, &SDL_DestroyWindow);
For anyone visiting this question after 12/2020, C++20 now includes an overloaded stream insertion operator<< for unique pointers ( as well as the overloaded operator<=> and it removed the operator!= ). make sure you are specifying -std=c++20 in the command line, makefile, or using c++20 in your IDE.
unique_ptr is one of the simpler stl classes - you can reimplement it and add a method to access the pointer inside.
The below code is an undefined behavior category ugly hack, but it actually works(tested in GCC using coliru online compiler with snippet below):
std::unique_ptr<myclass> uptr;
//Warning, undefined behavior!
myclass*& inside = *reinterpret_cast<myclass**>(&uptr);
Small test program to verify:
#include <iostream>
#include <memory>
class myclass {
public:
myclass() {
}
~myclass() {
std::cout << "It works!\n";
}
};
int main()
{
std::unique_ptr<myclass> uptr;
//Warning, undefined behavior!
myclass*& inside = *reinterpret_cast<myclass**>(&uptr);
//Warning, undefined behavior!
inside = new myclass();
return 0;
}

Syntax Error: Call Member Function Pointer using Pointer to Object

I have a tricky syntax error which I cannot figure out. I am trying to run a function delegate where the context is a pointer to an object.
Syntax error:
((object)->*(ptrToMember)) // the compiler doesn't like the ->*
Where object is of the type Component*
And ptrToMember is of the type void (Component::*EventCallback) ()
Below is the code with the syntax error:
typedef void (Component::*EventCallback) ();
...
std::weak_ptr<Component> wc( mySharedPtr );
EventCallback ec = &Component::run;
((wc.lock())->*(ec))(); // syntax error
(wc.lock()->(*ec))(); // simpler version but still syntax error
// This is ok, but is there any significant time or memory involved in this reference object creation?
Component& wcc = *wc.lock();
(wcc.*ec)();
wc.lock() returns a std::shared_ptr<Component> but you are expecting it to return a raw Component* pointer instead. You cannot call the ->* on the std::shared_ptr itself. You have to ask it for the Component* pointer it is holding, and then you can use the ->* operator on that pointer, eg:
(wc.lock().get()->*ec)();
Since you are dealing with a std::weak_ptr, which could expire before you use it, you should make sure the Component object is actually available after locking before you try to access it:
if (auto sptr = wc.lock()) {
(sptr.get()->*ec)();
}
else {
// wc had expired
}
The result of wc.lock() is a shared_ptr. This is one of the few cases where the smart pointer diverges from a dumb pointer. shared_ptr does not implement operator ->*, so your first syntax can't work. (It's not a syntax error, you're just trying to do something shared_ptr doesn't support.)
You've found a workaround though.

Cannot understand how to add new object to std::list<std::unique_ptr<classname>>

I have strange issue with std::list of unique_ptr's.
Class slFlyingMonster is derived from class slMonster.
Following code works:
std::unique_ptr<slMonster> ptr(new slFlyingMonster(md));
But this code:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.push_back(new slFlyingMonster(md));
throws error:
"Error 1 error C2664: 'void
std::list>,std::allocator>>>::push_back(const
std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : cannot convert
argument 1 from 'slFlyingMonster *' to
'std::unique_ptr> &&'"
While I understand, that something is wrong, like std::list.push_back() is not the same as =, but I cannot figure out how to correctly add new class as unique_ptr to list. Any suggestions would be very welcome.
Use push_back when you have an object of the type which your list contains, and you want to push its copy. Normally, if you don't have such an object yet (in your case, you don't), you're better off initialising a new object directly in the list — using emplace_back instead:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.emplace_back(new slFlyingMonster(md));
However, as #SebastianRedl correctly pointed out in the comments, the above has a problem of not being exception-safe. If the internal allocation of a new node inside std::list throws, the new slFlyingMonster instance would be leaked. emplace_back is not the correct choice when one of the arguments is an unprotected resource (such as a raw pointer owning memory).
So you actually want to construct a wrapper smart pointer and push it into the list. In C++14, you can do this with std::make_unique:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.push_back(std::make_unique<slFlyingMonster>(md));
With plain C++11, you can either implement your own make_unique, or explicitly create the smart pointer:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.emplace_back(std::unique_ptr<slMonster>(new slFlyingMonster(md)));
You may use emplace_back:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.emplace_back(new slFlyingMonster(md));
or push_back a std::make_unique:
std::list<std::unique_ptr<slMonster>> mMonsters;
mMonsters.push_back(std::make_unique<slFlyingMonster>(md));
or std::move of a std::unique_ptr
std::list<std::unique_ptr<slMonster>> mMonsters;
std::unique_ptr<slMonster> p(new slFlyingMonster(md));
mMonsters.push_back(std::move(p));
The constructor std::unique_ptr<T>(T*) is explicit, so T* cannot construct implicitly a std::unique_ptr.
Use mMonsters.emplace_back so the object is created from the argument given in parameters.

Why does one need a null shared_ptr and how can it be used?

In Scott Meyers's Effective C++, item 18 Make interfaces easy to use correctly and hard to use incorrectly, he mentioned the null shared_ptr:
std::tr1::shared_ptr<Investment> pInv(static_cast<Investment*>(0), getRidOfInvestment)
and a vogue assignment operation
pInv = ... //make retVal point to the correct object
In which case one may need to create a null shared_ptr and do assignment later? Why not just create the shared_ptr whenever you have the resources (raw pointer)?
Since Scott Meyers did not show the complete assignment in the previous example, I thought the shared_ptr's assign operator is overloaded that one can do this:
pInv = new Investment; // pInv will take charge of the pointer
// but meanwhile keep the delete function it already had
But I tried with boost's implementation it doesn't work this way. Then what is the sense to have null shared_ptr?
I am almost sure that I am missing something here, someone help me out of it please.
ps. more about the initialization and assignment of a shared_ptr
#include <boost/shared_ptr.hpp>
int main(int argc, char *argv[])
{
boost::shared_ptr<int> ptr1(new int);
boost::shared_ptr<int> ptr2;
ptr2.reset(new int);
boost::shared_ptr<int> ptr3 = new int;
return 0;
}
this example can not be compiled by g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 and the latest boost:
sptr.cpp: In function ‘int main(int, char**)’:
sptr.cpp:8:39: error: conversion from ‘int*’ to non-scalar type ‘boost::shared_ptr<int>’ requested
There is no need to use that hack to get a null (empty) shared_ptr. Simply use the default constructor:
std::shared_ptr<Investment> pInv; // starts null
To assign a pointer to a shared_ptr, either do it at construction time:
std::shared_ptr<Investment> pInt(new Investment);
// not allowed due to explicit annotation on constructor:
// std::shared_ptr<Investment> pInt = new Investment;
Or use the .reset() function:
pInt.reset(new Investment);
It's possible that the author of that article may have intended to provide a custom deleter (getRidOfInvestment). However, the deleter function is reset when .reset() is called, or when otherwise the inner pointer is changed. If you want a custom deleter, you must pass it to .reset() upon creation of the shared_ptr.
One pattern you might want to use to make this more foolproof is a custom creation function:
class Investment {
protected:
Investment();
// ...
public:
static shared_ptr<Investment> create();
};
shared_ptr<Investment> Investment::create() {
return shared_ptr<Investment>(new Investment, getRidOfInvestment);
}
Later:
shared_ptr<Investment> pInv = Investment::create();
This ensures you will always have the correct destructor function attached to the shared_ptrs created from Investments.
It's the same reason to have a null raw pointer - e.g.
say you have:
typedef std::tr1::shared_ptr<Investment> InvestmentPtr;
map<key,InvestmentPtr> portfolio;
...
get(mykey) {
iterator it = portfolio.find(mykey);
if (it == portfolio.end())
return InvestmentPtr();
else
return it->second;
}
}
This allows you to do:
InvestmentPtr p = get(key);
if (p) ...
There are tons of reasons you might like objects to be default constructible. First and foremost you'd like the smart pointer to be as similar as possible to a raw pointer, and since you can say int * p; (and get an undefined, uninitialized pointer), you can also say shared_ptr<int> p; and get a pointer that doesn't point anywhere (but you get to test it with !).
One of the most compelling reasons is possibly that you can make containers with shared_ptrs, and you can fill the containers without assigning pointees right there and then.