2017-01-14 10 views
2

я следующий код:C++ что происходит, когда функция возвращает вектор <shared_ptr<>>?

#include <iostream> 
#include <memory> 
#include <vector> 

class Test 
{ 
public: 
    Test() {} 
    ~Test() { std::cerr << "Delete\n"; } 
}; 

std::vector<std::shared_ptr<Test>> makeList() 
{ 
    std::vector<std::shared_ptr<Test>> list; 
    list.push_back(std::make_shared<Test>(Test())); 

    return std::move(list); 
} 

int main(int argc ,char **argv) 
{ 

    std::vector<std::shared_ptr<Test>> list; 
    std::cerr << "Before\n"; 
    list = makeList(); 
    std::cerr << "After\n"; 

    return 0; 
} 

Что я компилирую с:

clang++ -std=c++14 -o ptr ptr.cpp 

Выход есть:

Before 
Delete 
After 
Delete 

Мой вопрос: почему есть объект удаляется в функция makeList? Мое предположение заключалось в том, что list от функции будет перемещен в list от main и что, следовательно, ни один объект не будет удален/воссоздан в процессе?

Можно ли этого избежать (как очевидно, этот код не является оптимальным)?

+6

Создайте временный объект 'Test' с помощью' Test() '. Затем копия этого делается с помощью 'make_shared', затем временное уничтожается. Вероятно, вам понадобится 'std :: make_shared ()' –

+5

'return std :: move (list);' должен быть 'return list; 'по соображениям эффективности. –

+0

^^ Это. OP: Если ваш компилятор не предупреждает вас об этом, возможно, пора получить лучший компилятор (или включить предупреждения).Функция std :: move() здесь запрещает оптимизацию возвращаемого значения. Последние версии clang могут предупредить вас об этом. – marko

ответ

4

2 Изменения:

std::vector<std::shared_ptr<Test>> makeList() 
{ 
    std::vector<std::shared_ptr<Test>> list; 

    // make_shared does not need a copy of an object, just constructor arguments 
    list.push_back(std::make_shared<Test>()); 

    // return std::move(list) will defeat RVO. Never do that.  
    return list; 
} 

Таким образом, важной частью является:

list.push_back(std::make_shared<Test>(Test())); 

->

list.push_back(std::make_shared<Test>()); 

Просто для разъяснения, потому что у меня было то же самое сегодня, и мне не удалось увидеть разницу.

+0

Хорошо спасибо, я просто попробовал двигаться, потому что думал, что это будет иметь значение, но я попробовал оба варианта, поэтому на самом деле проблема возникает из-за того, что я создаю временный объект, когда я делаю вызов 'make_shared'. Большое спасибо. – user18490

3
list.push_back(std::make_shared<Test>(Test())); 

Здесь временно создан с Test(). Затем копируется c-tor для Test и временно уничтожается. Это первый вызов деструктора.

Второй вызов деструктора появляется в конце программы, когда list уничтожен.

правильная форма, чтобы избежать временного создания является:

list.push_back(std::make_shared<Test>()); 

Кроме того, вы не должны использовать std::move возвращения значения, потому что компилятор не может применить оптимизацию возвращаемого значения в таком случае.

2

Линия list.push_back(std::make_shared<Test>(Test())); делает временный Test, а затем перемещает его в фактический Test, построенный по std::make_shared<T>. Затем это временное уничтожение.

std::make_shared<T> требует аргументов, которые будут использоваться при строительстве T. По умолчанию построенный T просто не содержит аргументов.

Правильное использование, в данном случае, заключается в следующем:

list.push_back(std::make_shared<Test>());