0

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.

Nick
  • 9,962
  • 4
  • 42
  • 80

3 Answers3

2

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);
StenSoft
  • 9,369
  • 25
  • 30
  • Is this the way people do it? Coming from Java and old C++, is hard to believe nobody creates the class directly inside argument list :) – Nick Jun 28 '15 at 15:47
  • Well I guess the object could come from some factory function, but I agree, it still can be bind to a variable, before send to the function, even nobody will need ti after that. – Nick Jun 28 '15 at 15:54
  • 2
    @Nick If it comes from factory, you would use `unique_ptr` or `shared_ptr`. But I don't see much use for polymorphism on temporaries, lambdas are much more flexible. (Java uses polymorphism on temporaries for its anonymous classes, a thing we don't have in C++.) – StenSoft Jun 28 '15 at 15:57
  • final question before accepting the answer - is this the correct way? http://pastebin.com/YiMActsG , Isn't there some sugar for this? – Nick Jun 28 '15 at 16:23
  • @Nick I don't see any polymorphism on `Pair` there (`list` is lvalue reference anyway). If there is none indeed, use rvalue references. – StenSoft Jun 28 '15 at 18:20
1

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.

kfsone
  • 23,617
  • 2
  • 42
  • 74
  • Probably I am asking something that is obvious to everyone but me :) I am tryuing to do something like this http://pastebin.com/YiMActsG, if i do it without Pair p, it does not compile unless i use const ref in the function. Do i really need to use all these local variables? – Nick Jun 28 '15 at 21:36
  • So long as `IList::add()` isn't storing references, you can just do `list_add(list, Pair("3 city", "Sofia"))`. I'll edit my answer to explain – kfsone Jun 28 '15 at 22:44
0

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();
}
David Haim
  • 25,446
  • 3
  • 44
  • 78