На данный вопрос уже ответили:
Доброго времени суток!
Решил подтянуть lvalue/rvalue ссылки и столкнулся с copy/move семантикой. Не могу понять когда вызывается move-конструкторы и операторы перемещения.
Для примера написал это:
Интерфейс (Foo.h):
// class for testing rvalue and lvalue links and copy/move constructors and operator=
class Foo{
private:
int _length; // just one attribure
public:
Foo(); // default constructor
Foo(const int&); // constructor from length
~Foo(); // destructor
Foo(Foo&); // copy constructor
Foo(Foo&&); // move consttructor
Foo operator+(Foo&); // the meaning is clear from the title
Foo& operator=(Foo&); // copy =
Foo& operator=(Foo&&); // move =
};
Реализация (Foo.cpp):
#include <iostream>
#include "Foo.h"
Foo::Foo(){
std::cout << "\tCall Foo::Foo() -> default constructor\n";
_length = 0;
}
Foo::Foo(const int& length){
std::cout << "\tCall Foo::Foo(const int&) -> constructor\n";
_length = length;
}
Foo::~Foo(){
std::cout << "\tCall Foo::~Foo() -> destructor\n";
}
Foo::Foo(Foo& lvalue){
std::cout << "\tCall Foo::Foo(Foo&) -> copy constructor\n";
if (this != &lvalue){
_length = lvalue._length;
}
}
Foo::Foo(Foo&& rvalue){
std::cout << "\tCall Foo:Foo(Foo&&) -> move constructor\n";
if (this != &rvalue){
_length = rvalue._length;
rvalue._length = 0;
}
}
Foo Foo::operator+(Foo& right){
std::cout << "\tCall Foo+Foo\n";
return Foo(_length + right._length);
}
Foo& Foo::operator=(Foo& lvalue){
std::cout << "\tCall Foo::operator=(Foo&) -> copy =\n";
if (this != &lvalue){
_length = lvalue._length;
}
return *this;
}
Foo& Foo::operator=(Foo&& rvalue){
std::cout << "\tCall Foo::operator=(Foo&) -> move =\n";
if (this != &rvalue){
_length = rvalue._length;
rvalue._length = 0;
}
return *this;
}
}
Непосредственно вызовы (main.cpp):
#include <iostream>
#include "Foo.h"
using namespace std;
int main(){
int length = 7;
cout << "a :\n";
Foo a;
cout << "b :\n";
Foo b (3);
cout << "c :\n";
Foo c (length);
cout << "d :\n";
Foo d (c);
cout << "e :\n";
Foo e (с + d);
cout << "f :\n";
Foo f = Foo(length);
cout << "g :\n";
Foo g = g + d;
cout << endl;
}
Ну и вот так собираю (Makefile):
bin: main.o libfoo.so
g++ -o bin main.o -L. -lfoo -fPIC -std=c++11 -Wl,-rpath,.
main.o: main.cpp
g++ -c main.cpp -fPIC -std=c++11
libfoo.so: Foo.o
g++ -shared -o libfoo.so Foo.o -fPIC -std=c++11
Foo.o: Foo.cpp
g++ -c Foo.cpp -fPIC -std=c++11
clear:
rm -f *.so *.o bin
Вывод:
a :
Call Foo::Foo() -> default constructor
b :
Call Foo::Foo(const int&) -> constructor
c :
Call Foo::Foo(const int&) -> constructor
d :
Call Foo::Foo(Foo&) -> copy constructor
e :
Call Foo+Foo
Call Foo::Foo(const int&) -> constructor
f :
Call Foo::Foo(const int&) -> constructor
g :
Call Foo+Foo
Call Foo::Foo(const int&) -> constructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Возникает вопрос: почему не вызывается move-оператор и move-конструктор? Есть догадки по поводу того, что компилятор "оптимизирует" этот момент. Однако, точно я не уверен и хотел бы спросить совета у более подкованных в этом вопросе.
Спасибо!
Попробуйте такую main
:
Foo make()
{
return Foo{};
}
int main()
{
Foo x;
Foo f(move(x));
Foo g;
g = make();
}
Пояснить, почему в остальных случаях не вызываются перемещающие функции, несложно. a,b,c - думаю, очевидно. d - тоже (копируем c, а не перемещаем). В остальных случаях срабатывает оптимизация - не создается временное значение, создается сразу конечный объект. Кстати, фокус типа
Foo e (e + d);
наводит на нехорошие мысли об UB...
По-моему, так. (с) Пух
Foo func()
{
Foo result;
if (time(0) % 2)
{
Foo f(1);
return f;
}
return result;
}
int main(){
// ...
cout << "w :\n";
Foo w(move(g));
cout << "x :\n";
Foo x(func());
// ...
}
Вывод:
w :
Call Foo:Foo(Foo&&) -> move constructor
x :
Call Foo::Foo() -> default constructor
Call Foo::Foo(const int&) -> constructor
Call Foo:Foo(Foo&&) -> move constructor
Call Foo::~Foo() -> destructor
Call Foo::~Foo() -> destructor
Функция func() такая "хитрая" чтоб компилятор не схопнул цепочку конструкторов в "return Foo()" до одного, он имеет на это право (google: c++ Copy elision)
P.S.
Foo e (c + d);
тут нет деструктора от e + d, значит компилятор соптимизировал код так, что вызвался только конструктор для "e" от int Если оператор "+" усложнить, то компилятор не сможет так сделать. Например:
Foo Foo::operator+(Foo& right){
std::cout << "\tCall Foo+Foo\n" ;
Foo f(_length + right._length);
if (time(0) % 2)
{
f._length += 1;
}
return std::move(f);
}
Вывод:
Call Foo+Foo
Call Foo::Foo(const int&) -> constructor
Call Foo:Foo(Foo&&) -> move constructor
Call Foo::~Foo() -> destructor
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Подскажите, как можно сделать чтобы ввод и вывод был в функции ?
Написал небольшой класс который в будущем будет выступать в роли полноценного фреймворка, все работает, утечек вроде бы нет, нет никаких...
Допустим у меня есть сущность loan, у нее поле countryЯ хотел бы как-то реагировать на случай превышения некоторого числа запросов на секунду времени...