Reference and pointer in polymorphism - c++

Base abstract class:
class Satellite
{
public:
Satellite();
virtual void center()=0;
virtual ~Satellite(){}
};
First derived class
class Comm_sat:public Satellite
{
public:
Comm_sat();
void center() override{cout << "comm satellite override\n";}
};
Second derived class
class Space_station:public Satellite
{
public:
Space_station();
void center() override{cout << "space station override\n";}
};
Pointer version of the functions
void f(Satellite* ms){
ms->center();
delete ms;
}
int main()
{
Comm_sat* cs = new Comm_sat;
Space_station* ss = new Space_station;
f(cs);
f(ss);
}
The objects created using new in main() are properly destroyed in f(), right?
Reference version of the functions
void f(Satellite& ms){
ms.center();
}
int main()
{
Comm_sat cs;
Space_station ss;
f(cs);
f(ss);
}
Is the reference version better?
Besides, I try to use unique_ptr, however, I get errors
void f(Satellite* ms){
ms->center();
}
int main()
{
unique_ptr<Comm_sat> cs{new Comm_sat};
unique_ptr<Space_station> ss{new Space_station};
f(cs);
f(ss);
}
Error: cannot convert std::unique_ptr<Comm_sat> to Satellite* for argument 1 to void f(Satellite*)
Error: type class std::unique_ptr<Comm_sat> argument given to delete, expected pointer delete cs;
Same error for the other derived class.

Is the reference version better?
Yes, although a better way to put this would be "the pointer version is worse". The problem with the pointer version is that you pass it a valid pointer, and get a dangling pointer when the function returns. This is not intuitive, and leads to maintenance headaches when someone modifies your code thinking that you have forgotten to delete cs and ss in the main, not realizing that f deletes its argument.
The version that uses a reference is much better in this respect, because the resources are managed automatically for you. Readers of your code do not need to track the place where the memory of cs and ss gets released, because the allocation and release happen automatically.
I try to use unique_ptr, however, I get errors
There is no implicit conversion from std::unique_ptr<T> to T*. You need to call get() if you want to pass a raw pointer:
f(cs.get());
f(ss.get());

The objects created using new in main() are properly destroyed in f(), right?
They're destroyed, and cleaned up correctly, yes. "Properly" is a stretch though, since all this manual-new-and-delete-raw-pointers stuff is poor style.
The reason unique_ptr isn't working for you is that ... it's a unique_ptr, not a raw pointer. You can't just pass it as a raw pointer.
Try
void f(Satellite* ms){
ms->center();
}
// ...
f(cs.get());
or better, unless you really need to pass nullptr sometimes,
void f(Satellite& ms){
ms.center();
}
// ...
f(*cs);
or best of all, since you don't show any reason to require dynamic allocation at all:
void f(Satellite& ms);
// ...
{
Comm_sat cs;
f(cs);
} // no new, no delete, cs goes out of scope here

Related

Should I delete pointer from `new` passed to a function which makes into a `shared_ptr`?

In the following code example:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo *foo){
auto my_foo = std::shared_ptr<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(new Foo());
return 0;
}
Do I need to clean up the pointer created in main() by the bar.addFoo(new Foo) call, or will this be taken care of by Bar which creates a shared_ptr of it? My understanding is that auto my_foo = std::shared_ptr<Foo>(foo); will use the copy constructer to copy this pointer into my_foo leaving the original one dangling, is that correct?
The very idea of a constructor taking a raw pointer is to pass the ownership to std::shared_ptr. So, no, you don't have to delete a raw pointer passed to std::shared_ptr. Doing this will lead to a double deletions, which is UB.
Note that in general passing a raw pointer is dangerous. Consider the following more generalized example:
void addFoo(Foo *foo){
// some code which could throw an exception
auto my_foo = std::shared_ptr<Foo>(foo);
}
If an exception is throw before my_foo is constructed, foo will leak.
If you have no special reason to pass a raw pointer, consider the following alternative:
class Bar {
public:
template<class... Args>
void addFoo(Args... args){
auto my_foo = std::make_shared<Foo>(args...);
}
};
int main() {
auto bar = Bar();
bar.addFoo();
return 0;
}
Here you pass arguments (if you have any) to construct Foo inside addFoo() instead of constructing Foo before invoking addFoo().
Perfect forwarding of args... could be used if it is needed:
template<class... Args>
void addFoo(Args&&... args){
auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
}
The code you wrote is correct. But in modern C++, you should not be using raw pointers, new and delete unless you have to interoperate with code that does. If you can help it (and if question comments are any indication, you can), use smart pointers all the way through:
#include <iostream>
#include <memory>
class Foo {};
class Bar {
public:
void addFoo(std::unique_ptr<Foo> foo) {
auto my_foo = std::shared_ptr<Foo>(std::move(foo));
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_unique<Foo>());
return 0;
}
Above, the addFoo member function receives the pointer as a unique_ptr, and uses std::move to transfer ownership of the pointer from the unique_ptr to the shared_ptr without copying the referent; after constructing the shared_ptr, the unique_ptr is left in an empty state. You could also have addFoo receive a shared_ptr directly, or construct the object in-place inside the member function, as in Evg’s answer.
Using unique_ptr instead of a raw pointer makes it clear that the method intends to take ownership of the allocation, and encourages callers to use smart pointers themselves, making it less likely they will forget to delete their allocations later.
A raw pointer does not manage end of life, but a shared pointer does. When you create a shared pointer from a raw pointer, the shared pointer takes ownership of the object. That means that the object will be destroyed when the last shared pointer pointing to it will go out of scope.
In your code, my_foo takes ownership of the object created with new Foo(), goes out of scope when addFoo returns, and as it contains the only shared reference, correctly destroys the object.
The correct, c++ way to do this, would be the following:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo foo){
auto my_foo = std::make_shared<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(Foo());
return 0;
}
This avoids any raw pointers or naked new, and is totally exception safe. Also, std::make_shared introduces some performance benefits.
One confusing thing here is that the code seems to be unnecessarily copy the Foo object, however, since C++17, due to Return Value Optimization, (RVO), you are guaranteed to have no copies at all (when passing Foo as an argument to addFoo).
You can create the shared pointer with make_shared. If you want to construct Foo in main (e.g. because you have the paramters available there), then use make_shared at the point of construction and pass the shared_ptr on.
#include <iostream>
class Foo{
~Foo() { std::cout << "Foo destructed" << std::endl; }
};
class Bar{
public:
void addFoo(std::shared_ptr<Foo> foo){
auto my_foo = foo;
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_shared<Foo>());
return 0;
}
delete also calls your destructor. You can test, whether the shared pointer destructs your object or whether a delete is needed by printing out a message.

c++ translate from dynamic allocation to references

I have following code:
class A{
public:
virtual do_something() = 0;
}
class B : public A{
public:
virtual do_something() override;
}
void use_a(A *a){
if (a){
a->do_something();
delete a;
}
}
use_a( new B() );
How this can be translated to references?
Notice do_something() is not const method.
I thought it can be something like this:
void use_a(A &&a){
a->do_something();
}
use_a( B() );
but someone told me this is bad style and must be avoided.
Rvalue references have move sematics. That does not work well when moving B as A.
Use lvalue reference:
void use_a(A &a);
B b;
use_a(b);
or a template:
template <typename T>
void use_a(T &&a);
or, if it doesn't need to be a reference, a smart pointer:
void use_a(std::unique_ptr<A> a);
void use_a(std::shared_ptr<A> a);
Quite simply you convert from a pointer to a reference by providing a concrete instance, i.e. you dereference:
void f(int& i);
f(*(new int)); // do not do this!
The problem is that raw pointers in C++ are precisely that - they do not have automatic lifetime scope, and by converting to an lvalue reference, you have suggested a contract that the instance is concrete and should not be destroyed by the receiver.
int* ptr = new int;
f(ptr);
delete ptr; // otherwise it leaked
Modern C++ uses RAII to provide controlled automatic lifetime management, and C++11 introduced unique_ptr and shared_ptr for handling pointers. With C++14 we also have the mechanisms to avoid raw pointers entirely.
std::unique_ptr<int> ptr = std::make_unique<int>(/* ctor arguments here */);
f(ptr.get());
// now when ptr goes out of scope, deletion happens automatically.
See also http://en.cppreference.com/w/cpp/memory/unique_ptr
Only one std::unique_ptr should have the address of a given allocation at any time (it assumes ownership and will delete the allocation on exiting scope if it's not released).
For a ref-counted pointer: http://en.cppreference.com/w/cpp/memory/shared_ptr
--- EDIT ---
Based on the OPs comments:
Firstly note that
Pair p = { "one", "two" };
// and
Pair p("one", "two");
Pair p{"one", "two"};
are synonymous, in all cases they create a stack-local variable, p, by allocating stack space and calling Pair::Pair("one", "two") to construct a Pair object there.
Remember, however, that this is a stack variable - it has an automatic lifetime and will expire at the end of the current scope.
{ Pair p{"one", "two"}; list_add(list, p); } //p is destroyed
In theory, you can replace this with
list_add(list, Pair{"one", "two"});
But what matters is whether list_add expects you to keep the object around until you remove it from the list... That is often what a list-based function that takes a pointer is expecting. If it takes a non-const reference, it may do the same.
To answer your original post::
struct A { virtual void doSomething() {} };
struct B : public A { virtual void doSomething() override() {} };
void useDoSomethingInterface(A& a) {
a.doSomething();
}
int main() {
A a;
B b;
useDoSomethingInterface(a);
useDoSomethingInterface(b);
}
consider the following:
void list_add(IList& list, Pair& pair) {
pair.next = list.head;
list.head = &pair; // << BAD NEWS
}
void badness(IList& list) {
list_add(list, Pair("hello", "world"));
}
void caller() {
IList list;
badness(list);
// list.head now points to a destroyed variable on the stack
C-pointers in C++ are raw, machine level pointers. They don't ref count. And C++ object instances have a fixed well defined lifetime: till the end of the scope.
However, if list_add is taking its data by value
void list_add(IList& list, Pair pair)
Then we'll be ok. The temporary Pair we create will have to be copied once to create pair and then copied again into the list, which is a shame but at least it won't crash.
your code is a bit unsafe.
first, what if a is null? you didn't check it.
second, what if a points to a stack-object or data-segment-object? you'll have unexpected behaviour (=crash on most of the OS).
if your object has to be dynamically alocated, just use std::shared_ptr
void use_a(std::shared_ptr<A>& a){
a->do_something();
}

how to call a function when auto_ptr is a parameter of it!

#include <iostream>
#include <memory>
class test
{
private:
int x , y;
public:
test(int a , int b):x(a),y(b){}
void fun()
{
std::cout<< x<<" "<<y<<" "<<std::endl;
}
};
void show(std::auto_ptr<test> t1)
{
t1->fun();
}
int main()
{
show(new test(3,4));
}
I am getting a compilation error , please tell me what's wrong in this code?Thanks in advance.
Whenever you dynamically allocate an object, you should create a named smart pointer that immediately takes ownership of the object, then use that named smart pointer. For example,
std::auto_ptr<test> ptr(new test(3, 4));
show(ptr);
You cannot pass new test(3, 4) directly to the function because std::auto_ptr objects must be explicitly constructed; it would be quite unexpected if a smart pointer took ownership of an object implicitly.
That said, this is rather unusual anyway because when you call show(), the auto_ptr is copied and when auto_ptr is "copied," the original loses ownership and the copy gains ownership (so, after show() is called, you will find that ptr no longer has ownership of the object.
James explained how to resolve the problem.
But the reason auto_ptr was designed so that you can not do this:
show(new test(3,4));
Is because this is a bad idea.
Even this (if it were allowed):
show(std::auto_ptr<test>(new test(3,4)));
Would be a bad idea.
So you ask why.
Well in the normal situation not a big deal.
Bu what happens when you have a function that takes more than one parameter.
show2(std::auto_ptr<test>(new test(3,4)), SomeObject());
Now the standard gurantees that all parameters will be fully evaluated before the call (even the construction of the auto_ptr). But it does not guarantee the evaluation order nor that the evaluation will not be interleaved.
Thus it is possible for this evaluation:
// Phsedu code for parameter evaluation
test* tmp1 = new test(3,4);
SomeObject const& tmp2 = SomeObject();
std::auto_ptr<test> const& tmp3(tmp1);
call(tmp3, tmp1)
This order is bad. Because if the constructor of SomeObject throws an exception you will leak tmp1 as it has not yet been assigned to the std::auto_ptr.
This is why we auto_ptr was designed so that you have to give it a named variable.
std::auto_ptr<test> tmp3(new test(3,4));
SomeObject tmp1;
call(tmp3, tmp1)
Now if SomObject constructor throws the test object will be tidied up.

create an instance for a pointer in other scopes

I have two methods to create an instance for a pointer.
But one of them will fail.
class A {
public:
int num;
};
void testPointer1(A* a){
a = new A();
a->num = 10;
}
A* testPointer2(){
A* a = new A();
a->num = 10;
return a;
}
void testPointer() {
A* a1 = NULL;
testPointer1(a1); // this one fails
//cout << a1->num << endl; // segmentation fault
A* a2 = NULL;
a2 = testPointer2();
cout << a2->num << endl;
}
why is testPointer1 wrong?
The syntax is valid, but it doesn't do what you want because testPointer1() is operating on a copy of the pointer, not the actual pointer itself. So when you assign the address to the newly allocated object, it gets assigned to the copy, not to the original a1 pointer.
Because of this, the address is lost and you get a memory leak. Also, since the original a1 pointer was never modified in the first place, you attempted to dereference a null pointer, which is a bad thing.
I'd say testPointer2() is the better way to do it, but if you want testPointer1() to work, try this:
void testPointer1(A*& a)
{
a = new A();
a->num = 10;
}
The parameter type indicates "a reference to a pointer to A." That way, instead of a copy of the pointer being passed, a reference to the original pointer will be passed. A C++ reference is an alias to another object. So whatever you do on the alias, it gets performed on the original object.
Extra notes:
Note that the parentheses in new A(); are actually significant and their presence or absence makes a difference.
Also note that you must manually delete all new'ed objects after you're done with them, or you will get a leak. Typically you would wrap the pointer in its own class and implement RAII or use a smart pointer such as Boost's smart pointers or auto_ptr, for proper memory management and exception safety.
If you're going to set the value of num on initialization, why not create a constructor?
class A
{
public:
A(int n) : num(n) {}
int GetNum() const { return num; }
private:
int num;
};
void testPointer1(A*& a)
{
a = new A(10);
}
A* testPointer2()
{
return new A(10);
}
// auto_ptr example, see link in second note above
std::auto_ptr<A> testPointer3()
{
return auto_ptr<A>(new A(10));
}
The testPointer1 functions works on a copy of the provided pointer : modifications to a in testPointer1 are not reflected to the caller.
It's exactly like in this simpler example :
void testInt1(int i)
{
i++;
}
void testInt()
{
int i = 0;
testInt1(i);
// i is still 0
}
If you want the change in testInt1 to be reflected to the caller, you have to pass either a pointer or reference to i (and not just the value of i). The same solution can be applied to your specific case, though one could argue that pointers to pointer and references to pointer are not really a best practice.
Is this homework ?
This seems to be obvious:
formal parameters are saved on the stack & restored after method/function call.
then whatever f(type x), manipulating x inside the function/method won't change it's value outside of the function.
even if type is a pointer type.
the only way to make x change inside a function is to tell it is modifiable through references or pointer to type.
in your case :
A* a1 =NULL
call to your method won't change value of a1 outside of testPointer1
so a1 will still be NULL after the call.

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