I have a small segment of code that I need to optimize. Thread 'A' has created a boost shared_ptr to a heap object. Thread 'A' writes the shared_ptr to a thread safe queue. Thread 'B' reads the shared_ptr, uses it, and then destroys it.
Intense profiling/testing proves that the copying of the shared_ptr going in/out of the queue and adjusting reference counts is costly. Therefore, I would like to pass the shared ptr to the queue via reference. I would also like to use std::move to move the shared_ptr into the queue rather than construct a new shared_ptr, (I know this will invalidate the shared_ptr parameter which was passed to the queue).
Everything described works fine until I mix in a dash of polymorphism. I can't pass by ref a shared_ptr to a derived obj to a function expecting a shared_ptr to a base class. I have boiled this down to a very small snip that exposes the behavior that confuses me.
#include <boost/shared_ptr.hpp>
class Base
{
};
class Derived : public Base
{
};
int main()
{
boost::shared_ptr<Derived> pDerived(new Derived()); // simple creation
boost::shared_ptr<Derived> &alias1 = pDerived; // works fine
const boost::shared_ptr<Base> &alias2 = pDerived; // also works fine
boost::shared_ptr<Base> &alias3 = pDerived; // compilation error
//native pointers
Derived *alias4 = pDerived.get(); //works
const Base *alias5 = pDerived.get(); //works
Base *alias6 = pDerived.get(); //works
//native references
Derived &alias7 = *pDerived; // works
const Base &alias8 = *pDerived; // works
Base &alias9 = *pDerived; // works
}
I do not understand why the assignment to alias2 is perfectly fine, yet the assignment to alias3 yields a compiler error. Can someone please explain this? I need functionality like the alias3 example and can't make it work.
This problem is not related to boost or smart pointers. It can happen with POD types too as shown in the following simple example:
int main() {
int x = 0;
const double &y = x; //fine
double &z = x; //error!
return 0;
}
The reason is that a temporary value (rvalue) can bind to a const& but it cannot bind to a &. What happens in double &z = x is that x and z are unrelated types, and x needs to be converted to a double and a temporary variable is created, which cannot bind to a &.
Related
I am an beginner in C++ and would like to do something like this:
myObj f(){
// do stuff
// return instance of myObj
}
int main(){
// do stuff
myObj mO = f();
}
What would I have to do, to make this work in C++?
My thoughts were, that I would have to implement an assign operator for the myObj stuct/class or to write another constructor that looks like this myObj::myObj(myObj mO){...}, which I use like this myObj = myObj(f());.
Is this correct?
Do I have to do more, to make this work?
Could you maybe provide a working example?
Thanks!
That would almost compile as-is.
//define a class
class myObj {};
// return an instance of the class
myObj f() {
return myObj{};
}
// call with the same main as in the question:
int main(){
// do stuff
myObj mO = f();
}
C++ defines for you a copy constructor, an assignment operator and a move constructor if this can be done trivially; in these cases you should have to do nothing, just return an object instance and the caller will get it.
If the object has however some parts that cannot be copied (for example references) then you need to provide copy constructors and assignment yourself (but may be the class indeed shouldn't be copied or assigned).
There are also other limitations that prevent automatic synthesis of the move constructor (to avoid bugs).
Note also that there are cases in which the C++ compiler will synthesize copy constructor and assignment, but using wrong code. You need to be careful (for example if the class contains naked owning pointers).
For a simple case in which everything works out of the box with no need to do anything consider:
// A bi-dimensional point
struct P2d {
double x, y;
};
// Computes the middle point given two points
P2d average(P2d a, P2d b) {
return P2d{(a.x+b.x)/2, (a.y+b.y)/2};
}
As you see nothing is needed in the class to support returning P2d values or accepting P2d parameters.
The compiler in that case automatically completes the definition code to something like:
struct P2d {
double x, y;
P2d(const P2d& other)
: x(other.x), y(other.y)
{
}
P2d& operator=(const P2d& other) {
x = other.x;
y = other.y;
return *this;
}
~P2d() {
}
};
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
I am new to C++11. In fact until recently, I programmed only using dynamic allocation, in a way similar to Java, e.g.
void some_function(A *a){
a->changeInternalState();
}
A *a = new A();
some_function(a);
delete a;
// example 2
some_function( new A() ); // suppose there is **no** memory leak.
Now I want to reproduce similar code with C++11, but without pointers.
I need to be able to pass newly created class class A directly to function useA(). There seems to be a problem if I want to do so with non-const normal reference and It works if I do it with rvalue reference.
Here is the code:
#include <stdio.h>
class A{
public:
void print(){
++p; // e.g. change internal state
printf("%d\n", p);
}
int p;
};
// normal reference
void useA(A & x){
x.print();
}
// rvalue reference
void useA(A && x){
useA(x);
}
int main(int argc, char** argv)
{
useA( A{45} ); // <--- newly created class
A b{20};
useA(b);
return 0;
}
It compiles and executes correctly, but I am not sure, if this is the correct acceptable way to do the work?
Are there some best practices for this kind of operations?
Normally you would not design the code so that a temporary object gets modified. Then you would write your print function as:
void useA(A const & x){
x.print();
}
and declare A::print as const. This binds to both rvalues and lvalues. You can use mutable for class member variables which might change value but without the object logically changing state.
Another plan is to keep just A &, but write:
{ A temp{45}; useA(temp); }
If you really do want to modify a temporary object, you can write the pair of lvalue and rvalue overloads as you have done in your question. I believe this is acceptable practice for that case.
The best thing about C++11 move semantics is that most of the time, you get them "for free" without having to explicitly add any &&s or std::move()s in your code. Usually, you only need to use these things explicitly if you're writing code that does manual memory management, such as the implementation of a smart pointer or a container class, where you would have had to write a custom destructor and copy constructor anyway.
In your example, A is just an int. For ints, a move is no different from a copy, because there's no opportunity for optimization even if the int happens to be a disposable temporary. Just provide a single useA() function that takes an ordinary reference. It'll have the same behavior.
Is it ok to get the return value from dynamic_pointer_cast by reference, or can it cause problem?
struct A
{
};
struct B : public A
{
};
int main()
{
shared_ptr<A> b = make_shared<B>();
auto &a = dynamic_pointer_cast<A>(b);
//auto a = dynamic_pointer_cast<A>(b);
return 0;
}
Even if it were possible, it wouldn’t do what you want:
[The aim is that] the reference counter is not increased
It would still be increased since dynamic_pointer_cast returns a new shared_ptr copy anyway.
Your code doesn’t work since the reference would then be bound to the temporary object returned by the dyanamic_pointer_cast and this is forbidden.
Using a const reference would work (since const& is allowed to bind to a temporary) but the result would still be the same: the cast would create a new instance of a shared_ptr, and increment the reference count.
#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.