开发者

Init a shared_ptr of reference ( std::tr1::shared_ptr<class&> )

I'm using a library which returns a reference to me.

I need to use this reference as class-attribute.

Not being able to initialize the attribute in constructor directly (the lib needs to be inited before), I thought about using a shared_ptr for lazy initialization:

#include <iostream>
#include <string>
#include <tr1/memory>

//This is library, cannot touch
std::string globalString = "TEST";
std::string& getStringReference()
{
    return globalString;
}

//this is my class which uses the library
class Foo
{
public:
    Foo() :
        testString_( std::tr1::shared_ptr< std::string& >() )
    {
        //do some initialization of the library here...
        //now init the shared_ptr
        testString_.reset( getStringReference() );
    }

    std::string getString() const
    {
        return *testString_;
    }

private:
    std::tr1::shared_ptr< std::string& > testString_;
};

//and a main to be compilable and check if the above works...
int main()
{
    Foo foo;
    std::cout << foo.g开发者_运维问答etString() << std::endl;
}

But unfortunately this does not work. g++ gives messages like this:

error: forming pointer to reference type ‘std::string&’

I tried some other ways to get the reference into the shared_ptr, but nothing works... Perhaps you could give me a hint.

Note:

- In "real-world" instead of std::string the datatype is a class without default constructor.

- For those who still wonder: The above is just a simplified example-code :)

UPDATE:

- While trying to apply the suggestions, I found out that contrary to the used std::string from example, my class has a private copy constructor. This means I'm not able to just copy the object into a new one.


If you are provided by a reference only, that means that you are not responsible for memory management, and that in turns means that you cannot use your own memory management. That is, you should not use any type of smart pointer to hold that object.

While you can obtain the address of the real object with the & operator, doing so will cause undefined behavior later on when your shared_ptr goes out of scope and tries to free the memory and the library itself tries to free the memory on it's own.


You simply cannot take the address of a reference.

You can however use shared_ptr<string> and initialize it with &getStringReference. But that will cause the shared_ptr to try to delete the string, causing it to fail since it was never allocated using new. To fix this, make a copy:

testString_.reset(new std::string(getStringReference()))

Even better, let your class just store the reference directly. The you don't need to bother about memory management at all:

class Foo
{
    std::string& _testString;

    // ...
}


You need to understand the semantics of references and of memory management.

What you probably want is a raw pointer in your class. I assume you can't use a reference because you do not know what object it will refer to until you make the function call so you want to do something like ref=getRef();

You have no way of automatically "protecting" your pointer against becoming "dangling", but do not think there is anything you can do to rectify this situation. You just have to look at the documentation as to how to use the reference/pointer properly.


You could consider using Boost.Optional, which support optional references. This way, you could lazy initialize your reference without taking ownership of the referenced object as you do by using shared_ptr:

#include <iostream>
#include <string>
#include <boost/optional.hpp>

//This is library, cannot touch
std::string globalString = "TEST";
std::string& getStringReference()
{
    return globalString;
}

//this is my class which uses the library
class Foo
{
public:
    Foo()
    {
        //do some initialization of the library here...
        //now init the optional reference
        testString_.reset( getStringReference() );
    }

    std::string getString() const
    {
        return *testString_;
    }

private:
    boost::optional< std::string& > testString_; //<-- note the use of optional
};

//and a main to be compilable and check if the above works...
int main()
{
    Foo foo;
    std::cout << foo.getString() << std::endl;
}


Stop using shared_ptr now. Sharing is not an unilateral decision.

That being said, you are returned a reference to an object, not the object proper. There are two things you can do:

  • copy the object, and therefore deciding to manage the lifetime of the copy yourself
  • take a reference to the object, and let it up to the library you called to manage the lifetime, in which case you'll need to have some knowledge about it

I would definitely recommend copying unless there is a reason not to, much easier to get it right.

If you go for copying, use boost::optional<LibObject> within your class, this way you'll bypass the initialization issue.

If you wish to use a reference, you can still use boost::optional, and it'll work too! Use it as such: boost::optional<LibObject&>, but make sure the object you have a reference to will live long enough.

Finally, regarding the initialization issue, you could use a function to process the initialization and return a reference directly, thus initializing the reference of your class in the initialization list.


You can not use a shared_ptr with a reference type, because a reference is just that - a reference to an object. It is not a real object.

Next example is just simplified version of what you tried to do :

int main()
{
  int &* a;
}


Use a factory function?

class Library
{ 
 public:
  static void Init();
  static LibraryThing & getThing();
};


class Foo
{
  public:
  static shared_ptr<Foo> Create()
  {
   if (!_libInit)
   {
    Library::Init();
    _libInit = true;
   }
   return make_shared(new Foo(Library::getThing()));
  }

 private:
  Foo(LibraryThing & ref) : _ref(ref)
  {
  }

 private:
  LibraryThing & _ref;
  static bool     _libInit;
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜