here is a "chess++" problem that I'm facing wright now with my nested class,
although it may look like some joke, it's not a joke but real problem which I want to either solve or change the way to achieve the same thing in my project.
#include <map>
#include <memory>
#include <iostream>
#include <sigc++/signal.h>
class foo
{
public:
struct bar;
typedef sigc::signal<void, std::shared_ptr<bar>> a_signal;
struct bar
{
bar()
{
some_signal.connect(sigc::mem_fun(*this, &foo::bar::func));
}
void notify()
{
some_signal.emit(this); // how to ??
}
void func(std::shared_ptr<foo::bar> ptr)
{
std::cout << "you haxor!" << std::endl;
// use the pointer ptr->
}
a_signal some_signal;
};
std::map<int, std::shared_ptr<bar>> a_map;
};
int main()
{
std::shared_ptr<foo::bar> a_foo_bar;
foo foo_instance;
foo_instance.a_map.insert(std::pair<int, std::shared_ptr<foo::bar>>(4, a_foo_bar));
foo_instance.a_map.at(0)->notify();
return 0;
}
What I want to do here is to emit a signal.
the signal is declared as one that triggers a handler that takes a shared_ptr as an argument.
the function notify() should convert *this into shared_ptr, how do I do that to make the above code run?
Derive from enable_shared_from_this:
struct bar : std::enable_shared_from_this<bar>
to get a member shared_from_this():
some_signal.emit(shared_from_this());
As long as the current object is owned by at least one shared pointer, this will return a shared pointer, sharing ownership with that pointer. Note that, in your program, a_foo_bar is empty, so neither this nor the call to notify will work. Also beware that it won't work from the constructor or destructor, since the object is not owned by a shared pointer at that time.
Related
I'm trying to learn the basics of C++, and like everyone learning pointers is hard.
So, I'm trying out the C++14 std::unique_ptr class, and this is probably a silly question.
#include <iostream>
#include <memory>
struct Foobar {
bool active = false;
};
int main()
{
std::unique_ptr<Foobar> foobar = std::make_unique<Foobar>();
Foobar foo = *foobar;
foo.active = true;
Foobar bar = *foobar;
// prints zero and not one
std::cout << bar.active << std::endl;
}
Dereferencing my pointer and changing the bool in the struct doesn't change the actual underlying value in memory. Why is that so?
What basic thing am I missing?
Foobar foo = *foobar;
foo.active = true;
This makes a copy of the object referenced by foobar, and stores it in a new variable called foo; then modifies foo's active flag.
Of course, this does absolutely nothing to the original object that's (still) referenced by foobar.
The code then makes another copy of the object, and prints the unmodified value of its active flag.
Since std::istream can't be moved (protected function), I was trying to wrap the std::istream so that I can build my code upon customized stream factory.
So far I have tried inheriting directly from std::istream like this:
class IStream : public std::istream{
public:
// template <typename T>
IStream(std::istringstream&& rhs) : std::istream(std::move(rhs)){
this->rdbuf(rhs.rdbuf());
rhs.istream::rdbuf(NULL);
}
IStream(IStream&& rhs) : std::istream(std::move(rhs)){
this->rdbuf(rhs.rdbuf());
rhs.rdbuf(NULL);
}
};
But it causes a segmentation fault (any insights on the reason would be appreciated), so I move on to some "safer-looking" method.
Here is the code I currently use:
#include <iostream>
#include <sstream>
#include <istream>
#include <string>
#include <memory>
#include <cassert>
using namespace std;
class IStream {
public:
IStream(const IStream& rhs) = delete;
template <typename T>
IStream(T& rhs) : shared_(rhs) {
std::cout << "called IStream(T&)" << std::endl;
}
// assume strict order between member construct for now
template <typename T>
IStream(T&& rhs) : owned_{std::make_unique<T>(std::move(rhs))}, shared_(*owned_) {
std::cout << "called IStream(T&&)" << std::endl;
}
IStream(IStream&& rhs) : owned_(std::move(rhs.owned_)), shared_(*owned_) {
assert(rhs.owned_.get() == nullptr); // failed
std::cout << "called IStream(IStream&&)" << std::endl;
}
std::istream& get() {
return shared_;
}
~IStream() {
std::cout << "called ~IStream with " << (owned_.get()!=nullptr) << std::endl;
}
private:
std::unique_ptr<std::istream> owned_;
std::istream& shared_;
};
IStream&& wrap() {
return IStream(istringstream{"test"});
}
int main(void) {
IStream is(wrap());
char buf[10];
memset(buf, 0, sizeof(char) * 10);
is.get().getline(buf, 10);
std::cout << std::string(buf) << std::endl;
return 0;
}
Sad thing is this code still won't work, and I found that assertion at IStream::IStream(IStream&&) failed.
Output:
called IStream(T&&)
called ~IStream with 1
Assertion failed: rhs.owned_.get() == nullptr, file .\tmp.cpp, line 23
Which leads to wierd phenomenon where unique_ptr is not null after moved.
I am using MSVC compiler btw.
You cannot sanely do what you want.
As I understand it, you want some sort of object that is as indistinguishable from a ::std::istream as possible, but has move semantics so that it will be automatically destroyed after it's no longer needed.
The primary useful attribute of anything that can be referred to as an ::std::istream is that it is derived from ::std::istream, and since it is ::std::istream that can't be moved, nothing derived from it can be moved either.
So, you're left with the next best thing, a ::std::unique_ptr to your ::std::istream thing. This means you'll have to use * all the time and it will be ugly. Oh, well.
No amount of cleverness will allow you to create any kind of wrapper that is simultaneously moveable, and derived from ::std::istream so that it works with all the nice existing library functions that expect that. Nor can you make something that acts in any reasonable way like a reference without that thing actually being a reference.
BTW, when you construct a unique_ptr you can provide a custom deleter if you're actually pointing at something like ::std::cin that you don't want to delete. You can make the custom deleter simply do nothing in that case. The custom deleter will be moved right along with the pointer when you move your unique_ptr around.
This function returns a dangling reference:
IStream&& wrap() {
return IStream(istringstream{"test"});
}
The temporary object is destroyed when the function returns. Instead change the function to return by value, IStream wrap() {.
Also, shared_(*owned_) leads to a dangling reference because that refers to the memory location where the currently-owned object resides, even if that object is destroyed and the unique_ptr is later changed to own a different object.
It would be a good idea to get rid of shared_, and just call owned_.get() as needed.
I have a class like this :
Header:
class CurlAsio {
public:
boost::shared_ptr<boost::asio::io_service> io_ptr;
boost::shared_ptr<curl::multi> multi_ptr;
CurlAsio();
virtual ~CurlAsio();
void deleteSelf();
void someEvent();
};
Cpp:
CurlAsio::CurlAsio(int i) {
id = boost::lexical_cast<std::string>(i);
io_ptr = boost::shared_ptr<boost::asio::io_service>(new boost::asio::io_service());
multi_ptr = boost::shared_ptr<curl::multi>(new curl::multi(*io_ptr));
}
CurlAsio::~CurlAsio() {
}
void CurlAsio::someEvent() {
deleteSelf();
}
void CurlAsio::deleteSelf() {
if (io_ptr) {
io_ptr.reset();
}
if (multi_ptr)
multi_ptr.reset();
if (this)
delete this;
}
During run time, many instances of CurlAsio Class is created and deleted.
So my questions are:
even though I am calling shared_ptr.reset() , is it necessary to do so ?
i monitor the virtual memory usage of the program during run time and I would expect the memory usage would go down after deleteSelf() has been called, but it does not. Why is that?
if i modify the deleteSelf() like this:
void CurlAsio::deleteSelf() {
delete this;
}
What happens to the two shared pointers ? do they get deleted as well ?
The shared_ptr members have their own destructor to decrement the reference count on the pointee object, and delete it if the count reaches 0. You do not need to call .reset() explicitly given your destructor is about to run anyway.
That said - why are you even using a shared_ptr? Are those members really shared with other objects? If not - consider unique_ptr or storing by value.
As for memory - it doesn't normally get returned to the operating system until your program terminates, but will be available for your memory to reuse. There are many other stack overflow questions about this.
If you're concerned about memory, using a leak detection tool is a good idea. On Linux for example, valgrind is excellent.
if i modify the deleteSelf() like this:
void CurlAsio::deleteSelf() {
delete this;
}
Don't do this. This is an antipattern. If you find yourself "needing" this, shared_from_this is your solution:
Live On Coliru
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
#include <vector>
struct X : boost::enable_shared_from_this<X> {
int i = rand()%100;
using Ptr = boost::shared_ptr<X>;
void hold() {
_hold = shared_from_this();
}
void unhold() { _hold.reset(); }
~X() {
std::cout << "~X: " << i << "\n";
}
private:
Ptr _hold;
};
int main() {
X* raw_pointer = nullptr; // we abuse this for demo
{
auto some_x = boost::make_shared<X>();
// not lets addref from inside X:
some_x->hold();
// now we can release some_x without destroying the X pointed to:
raw_pointer = some_x.get(); // we'll use this to demo `unhold()`
some_x.reset(); // redundant since `some_x` is going out of scope here
}
// only the internal `_hold` still "keeps" the X
std::cout << "X on hold\n";
// releasing the last one
raw_pointer->unhold(); // now it's gone ("self-delete")
// now `raw_pointer` is dangling (invalid)
}
Prints e.g.
X on hold
~X: 83
I'm new to shared_ptr's and I'm trying to figure out the exact functionality of the .reset() function.
#include <memory>
#include <stdio>
using namespace std;
class SomeClass{};
int main()
{
shared_ptr<SomeClass> sp (nullptr);
//do some stuff, sp now has 10 co-owners
cout << sp.use_count << endl;
sp.reset();
cout << sp.use_count << endl;
return 0;
}
Would output
10
0
So since I used the reset function are all instances deleted from memory? As in, have I just eliminated any possible memory leaks with sp? Obviously this was a toy example that I quickly made up, sorry if it has any errors.
Follow up situation:
shared_ptr<SomeClass> returnThis() {
shared_ptr<SomeClass> someObject(new SomeClass(/*default constructor for example*/) );
return someObject;
}
somehere in main:
shared_ptr<SomeClass> mainObject;
mainObject = returnThis();
Does mainObject have a use count of 2 because someObject was created in a function but never cleared? Or is it one and the clean-up is done automatically when returning the value?
When you use .reset(), you are eliminating one owner of the pointer, but all of the other owners are still around. Here is an example:
#include <memory>
#include <cstdio>
class Test { public: ~Test() { std::puts("Test destroyed."); } };
int main()
{
std::shared_ptr<Test> p = std::make_shared<Test>();
std::shared_ptr<Test> q = p;
std::puts("p.reset()...");
p.reset();
std::puts("q.reset()...");
q.reset();
std::puts("done");
return 0;
}
The program output:
p.reset()...
q.reset()...
Test destroyed.
done
Note that p and q are both owners of the object, and once both p and q are reset, then the instance is destroyed.
No.
The whole purpose of shared_ptr is that you cannot delete it from one place if someone is using it in another. shared_ptr::reset() just decreases use_count by one and replaces its object by nullptr.
The .reset() method only applies to the object it's called upon.
It just replaces the pointer that variable is holding.
I'm trying to make a kind of screen manager in C++ but I'm getting errors.
With my code below I receive
1>screenmanager.cpp(26): error C2664: 'void std::vector<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from 'virtualGameScreen' to 'virtualGameScreen *&&'
1> with
1> [
1> _Ty=virtualGameScreen *
1> ]
1> Reason: cannot convert from 'virtualGameScreen' to 'virtualGameScreen *'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>
Errors occur with gameScreen.push_back(gameScreenToAdd);
I get access violation error when adding a reference operator with gameScreenToAdd.
ScreenManager.h
void AddScreen(virtualGameScreen);
void RemoveScreen(virtualGameScreen);
ScreenManager.cpp
std::vector<virtualGameScreen*> gameScreen;
void ScreenManager::Initialize(void)
{
MainMenu menu = MainMenu();
AddScreen(menu);
}
void ScreenManager::AddScreen(virtualGameScreen gameScreenToAdd)
{
gameScreenToAdd.LoadContent();
gameScreen.push_back(gameScreenToAdd);
}
So, I've hit a bit of a wall, any suggestions on how I might fix this?
edit game runs if I change gameScreen.push_back to gameScreen.push_back(new MainMenu()); buut that's not really how I want the function to work
So, the first thing the compiler did is tell you where the problem occurs:
1>screenmanager.cpp(26)
It also told you primarily what the problem is:
Reason: cannot convert from 'virtualGameScreen' to 'virtualGameScreen *'
So - something in your code is providing a "virtualGameScreen" object instance where it was expecting a pointer (denoted by the *). And it's on line 26. The other parts of the error indicate it's the call to push_back. Lets look at line 26:
gameScreen.push_back(gameScreenToAdd);
Yep - you're calling push_back, and you're passing it gameScreenToAdd, which is of type virtualGameScreen. The push_back call is from this vector:
std::vector<virtualGameScreen*> gameScreen;
Your vector expects pointers, so the push_back expects vectors.
HOWEVER: You can't just do this:
gameScreen.push_back(&gameScreenToAdd);
because gameScreenToAdd is a temporary function variable - when you call AddScreen, the original variable is copied into a new, temporary virtualGameScreen for the lifetime of the function call. That means when the program leaves AddScreen the screen whose address you pushed will no-longer exist (the memory is still there, but it has been released and the computer will now proceed to use it for other reasons).
What you'll need to do is change AddScreen to take a pointer.
void ScreenManager::AddScreen(virtualGameScreen* gameScreenToAdd)
{
gameScreenToAdd.LoadContent();
gameScreen.push_back(gameScreenToAdd);
}
Unfortunately, this leaves you open to yet another problem with your code.
void ScreenManager::Initialize(void)
{
MainMenu menu = MainMenu();
AddScreen(menu);
}
This function creates a temporary, local MainMenu object - with a lifetime of the duration of Initialize. Then it creates a second, temporary MainMenu and copies it to menu.
If you write
AddScreen(&menu);
it will work, but it will pass the address of a temporary instance to AddScreen.
As soon as program flow leaves the "Initialize()" function, your value goes away.
It looks like you may have some prior experience with something like Java or C# and are trying to apply previous knowledge to C++.
What you need is a member variable to store "Menu" for the life time of the instance of ScreenManager.
Option 1: Just use a class member variable.
class ScreenManager
{
MainMenu m_menu;
public:
ScreenManager()
: m_menu() // initialize menu while we are initializing.
{}
void Initialize()
{
AddScreen(&m_menu);
}
// ...
};
If you really want to use a pointer, you might do the following:
class ScreenManager
{
MainMenu* m_menu;
public:
ScreenManager()
: m_menu(nullptr) // make sure it's null as soon as the object is created
{}
void Initialize()
{
m_menu = new MainMenu();
AddScreen(m_menu);
}
// but now we have to make sure it is released when we go away
~ScreenManager()
{
if (m_menu)
{
delete m_menu;
m_menu = nullptr;
}
}
};
Option 3: use C++ containers to manage the lifetime of the pointer for you, either std::unique_ptr or std::shared_ptr
---- EDIT ----
Seeing the edit you made while I was writing this, it's a little clearer what you're trying to do. What you probably want is something more like this:
std::vector<std::unique_ptr<virtualGameScreen>> gameScreen;
Consider the following:
Live demo: http://ideone.com/7Th2Uk
#include <iostream>
#include <vector>
class Foo {
const char* m_name;
public:
Foo(const char* name) : m_name(name) { std::cout << "Foo " << m_name << '\n'; }
~Foo() { std::cout << "~Foo " << m_name << '\n'; }
};
int main() {
std::vector<Foo*> foos;
Foo foo("foo");
foos.push_back(new Foo("new"));
return 0;
}
Note that the second foo is never released.
Foo foo
Foo new
~Foo foo
std::unique_ptr is a pointer-container object which will delete the object when the object expires. This makes it suitable for use in a container like std::vector
#include <iostream>
#include <vector>
#include <memory> // for std::unique_ptr
class Foo {
const char* m_name;
public:
Foo(const char* name) : m_name(name) { std::cout << "Foo " << m_name << '\n'; }
~Foo() { std::cout << "~Foo " << m_name << '\n'; }
};
int main() {
std::vector<std::unique_ptr<Foo>> foos;
Foo foo("foo");
foos.emplace_back(new Foo("new"));
return 0;
}
Both objects get cleaned up:
Foo foo
Foo new
~Foo foo
~Foo new
Now you don't need your m_menu at all, you can simply call AddScreen with a 'new MainMenu()' and the pointer will be added to the vector such that when the vector goes out of scope, proper cleanup will happen.
Menu* menu = new MainMenu();
AddScreen(menu);
or
AddScreen(new MainMenu());
In theory what you should really do is ensure that the allocation goes straight into a unique_ptr object so that there's no window for it to get leaked, but teaching the use of std::unique_ptr is beyond the scope of this answer. http://msdn.microsoft.com/en-us/library/hh279676.aspx, http://www.drdobbs.com/cpp/c11-uniqueptr/240002708, etc.
In pre-C++11 code, you might have something like this:
std::vector<virtualGameScreen*> gameScreen;
void ScreenManager::Initialize(void)
{
AddScreen(new MainMenu);
}
void ScreenManager::AddScreen(virtualGameScreen *gameScreenToAdd)
{
gameScreenToAdd->LoadContent();
gameScreen.push_back(gameScreenToAdd);
}
but you would have to have some way to make sure the object got deleted.
With C++11, you would probably want to have the memory managed automatically:
std::vector<std::unique_ptr<virtualGameScreen>> gameScreen;
void ScreenManager::Initialize(void)
{
AddScreen(std::unique_ptr<MainMenu>(new MainMenu));
}
void ScreenManager::AddScreen(std::unique_ptr<virtualGameScreen> gameScreenToAdd)
{
gameScreenToAdd->LoadContent();
gameScreen.emplace_back(std::move(gameScreenToAdd));
}
That's because you did not provide a pointer to the vector (gameScreen), and another issue about the code is that: the paramater will generate a temp object, if just put the address of it the app maybe crash.