2015-04-03 2 views
2

Предположим, я пишу класс, который можно было бы повторно использовать в различных контекстах. Его конструктор принимает определенный пользователем тип, который я храню в переменной-члене. Даже для объектов, которые, как мне известно, дешевы для копирования, я пытаюсь решить, лучше ли принимать аргумент по значению или указателем.Должен ли я хранить аргумент конструктора по значению или указателем?

// By value 
class MyType 
{ 
private: 
    SomeUdt udt_; 

public: 
    MyType(SomeUdt udt) : 
     udt_ {udt} 
    {} 

    // ... 
}; 

против

// By pointer 
class MyType 
{ 
private: 
    std::shared_ptr<SomeUdt> udt_; 

public: 
    MyType(std::shared_ptr<SomeUdt> udt) : 
     udt_ {udt} 
    {} 

    // ... 
}; 

Если я его (общий) указатель, то я почти наверняка заставляя клиента выделить этот объект в куче. (Технически они могли бы выделить в стеке и все еще послать мне указатель, но это было бы замутить собственности и жизни.)

И если я возьму его значение, то я удалю их способность управлять экземплярами, например, если клиент хотел, чтобы SomeUdt был синглом.

Каков наилучший подход по умолчанию?

EDIT:

Пару других интересных моментов в этом анализе.

Во-первых, комментатор отметил, что если я возьму по указателю, то мои личные данные не будут полностью закрытыми. Клиент имеет доступ к моему объекту-члену.

Кроме того, если пользовательский тип должен быть полиморфным и иметь любые чистые виртуальные элементы, то, я думаю, у вас нет выбора, кроме как использовать указатели.

+1

Если вы передадите его в качестве указателя, клиент может изменить личную переменную через этот указатель. – kvorobiev

+0

по значению - затем переместите его на место. –

+0

Что не так с шаблоном Явный MyType (Us && ... Vs): udt_ (std :: forward (Vs) ...) {} 'Является ли ваш контейнер шаблоном? Если это шаблон, сохраните его по значению, а _client_ может создавать экземпляр с указателем, если это то, что они хотят: 'MyType >'. Зачем нужен этот объект? –

ответ

0

Если не хорошая причина для чего-то другого, предпочитают этот подход:

// By value, using move 
class MyType 
{ 
private: 
    SomeUdt udt_; 

public: 
    MyType(SomeUdt udt) : 
     udt_ { std::move(udt) } 
    {} 

    // ... 
}; 
+0

Я рекомендую разбивать на перегрузки const-ref и r-ref, когда у вас есть один аргумент, чтобы оптимизировать недвижущиеся типы. –

+0

... или мы могли бы просто обновлять устаревшие типы для перемещения ... –

+0

'std :: array '? –

0

Я думаю, что если вы делаете изменяющие операции над данными, то лучше идти по значению. Если ваш класс просто действует как контейнер/считыватель, то по ссылке идеально.

2

Оба подхода имеют различную семантику:

  1. Взятие SomeUdt говорит: «Я хочу, чтобы ценность SomeUdt

    В этом случае клиентский код может выдавать значение, которое ему нравится - возможно, из временного объекта или автоматически распределено или динамически распределено. Это не имеет значения, потому что это не главный объект, но это значение . После того, как значение было предоставлено, клиент и MyType не делят ни одного состояния.

  2. Принимая std::shared_ptr<SomeUdt>, он говорит: «Я хочу передать право собственности на динамически выделенный объект SomeUdt».

    В этом случае клиентский код можно было бы ожидать, чтобы динамически выделять SomeUdt объект, управлять своей жизни с shared_ptr, а затем разделить право собственности на этот объект с MyType. Как только объект был предоставлен, клиент и MyType делят какое-то состояние.

Если у вас нет оснований для этого, вы должны предпочесть передавать объекты по значению. Ценностная семантика и локальное состояние всегда хорошие. Вы должны использовать shared_ptr только в том случае, если требуется семантика совместного использования.

Помните, что если SomeUdt тип подвижны, это будет более эффективно делать:

MyType(SomeUdt udt) : 
    udt_ {std::move(udt)} 
{} 

И если я возьму его значение, то я удалю их способность управлять экземплярами, например, если клиент хотел SomeUdt быть синглом.

Как MyType сохраняет свое состояние - это свое дело. Зачем нужен код клиента? Он должен быть заключен в MyType.

Кроме того, если пользовательский тип должен быть полиморфным и иметь любые чистые виртуальные элементы, то я думаю, что у вас нет выбора, кроме как использовать указатели.

Если вы хотите полиморфизм, то вы по сути не можете запрашивать значение объекта, потому что вы не знаете, каким будет тип этого значения. В этом случае я бы предложил взять SomeUdt& или SomeUdt const& (предполагая, что SomeUdt является базовым классом). Затем он говорит: «Мне нужен объект SomeUdt» и не спрашивает о том, как этот объект был создан.

Выполнение shared_ptr предназначено исключительно для совместного использования динамически распределенных объектов. Он делает предположения об общем объекте.

Смежные вопросы