2009-10-20 2 views
1

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

однако я определяю его, это правда?

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

Я хотел бы разработать поглотитель, где я могу вернуть мою приватную переменную и быть уверенным, что вызываемая не может удалить его ...

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

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

Thanks,

Джо

Мой другой вопрос не был действительно сосредоточены, так что я сделал это снова, его не проблема, спрашивает вещи здесь, не так ли? =]

+1

Почему вы не можете вернуть копию вместо ссылки? – JRL

+0

Является ли возврат по стоимости опцией? – coelhudo

+0

Я могу, но это не лучший ответ, я думаю. Я думаю, что буду использовать boost или что-то еще, прежде чем полагаться на копии – Jonathan

ответ

5

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

И если у вас есть ссылка, вы можете взять адрес этого, который дает вам указатель

Во всяком случае, если у вас есть этот класс, например:

class X { 
    int getByVal() { return i; } // returns a copy of i 
    int& getByRef() { return i; } // returns a reference to i 
private: 
    int i; 
}; 

тогда я, как пользователь вашего класса, не имеет Очевидный способ удалить ваши данные. я могу сделать следующее:

X x; 
int j = x.getByVal(); 
int& k = x.getByRef(); 
j = 42; // doesn't affect x.i because we returned a copy 
k = 42; // sets x.i to 42, because k is a reference 

И нет никакого очевидного способа для меня, чтобы удалить члена класса. Конечно, я мог сделать это:

delete &j; 
delete &k; 

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

«Защитите свой код от Мерфи, а не Макиавелли», как правило, хорошее эмпирическое правило. Вы не можете помешать людям разрушить ваш код , если они попробуют. Все, о чем вы должны беспокоиться, мешает им делать это случайно.

Редактировать
В ответ на ваш комментарий под вопросом:

как я уже говорил, я учусь ... Копия сделать думают, что вызываемое должен освободить память возвращения переменного, это больше неприятностей для вызываемого (даже подумал, что это я = р), поэтому я не говорил о концепциях, а о легкости письма ... и снова, я новичок в этой памяти. Я развивался в C#, PHP и т. Д. Я давно развивался в C, когда учился с CircleMUD

Нет, копии не нужно удалять вручную. Локальные переменные автоматически удаляются, когда они выходят за рамки. Таким образом, в приведенном выше примере j является копией члена класса i. Когда функция вызова возвращается, j будет автоматически удалена.

Надеюсь, что это поможет. Правила переменной жизненного цикла в C++ не очень сложны, но крайне важно, чтобы их правильно, так как от них зависит много кода.

void foo() 
{ 
    int i = 0; // allocate a local (on the stack) int, and initialize it to 0 
    int* p = new int(1); // allocate an int on the heap, and initialize it to 1 
    int j = i; // create a *copy* of i. Now we have two ints on the stack 
    int k = *p; // create a copy of the int pointed to by p. k is also on the stack, so even though it was copied from a heap-allocated variable, k does not have to be manually deleted 
    int* q = p; // create a copy of p. q is not a separate pointer, which points to the *same* heap-allocated integer. 
} 

в приведенном выше примере, все копии автоматически очищаются, когда foo возвращается. Единственное, что нам нужно сделать вручную - удалить целое число, выделенное в куче. Оба указывают на него p и q, но мы должны только удалить объект один раз. Но i, j, k, p и q - все локальные переменные, объявленные в стеке. Каждый из них очищается, когда функция возвращается. Для примитивных типов (например, int s, а также указателей) ничего не должно произойти (у них нет деструкторов). Когда они выходят из сферы действия, они просто исчезают, даже если они указывают на что-то важное, например, на выделенный кучей объект, такой как наше целое число.

Для объектов, отличных от POD, когда они выходят из сферы действия, их деструкторы называются, поэтому они тоже хорошо очищаются, все сами по себе. Таким образом, даже если бы мы использовали более сложный тип, чем int, вышеизложенное было бы прекрасно. Мы все еще можем копировать объекты не-POD и передавать их по значению.

Я надеюсь, что это поможет немного разобраться.

+2

+1 для ссылки на совет Саттера «Мерфи против Макиавелли» –

+0

Это откуда? Я просто знаю, что это хорошая цитата: D – jalf

+0

Ну, в первый раз я увидел, что это выражение было в его книге Exceptional C++ –

0

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

+0

не по значению только для POD? – Jonathan

+1

Нет, хотя это наиболее распространено с ними. Но, например, итераторы и объекты функций рассматриваются как дешевые для копирования в std lib и, таким образом, много копируются. И это не POD. – sbi

+0

Нет. Вы можете вернуть что-либо по стоимости. Если данные не являются POD, он будет ссылаться на конструктор копирования. –

1

Для этого вы можете использовать слабые указатели. Слабые указатели ссылаются на ресурс, принадлежащий интеллектуальному указателю обмена без добавления к его счету ссылок. (Тем не менее, до тех пор, пока пользователи могут получить голый указатель от умного указателя - и без этого, это будет только smart, но не указатель больше -, они могут вызывать delete. Но это нормально: если люди хотят разрушить их код, они всегда найдут способы сделать это. Посмотрите на свою цель, чтобы Мерфи не выполнял свою работу, а не Macchiavelli.

Конечно, вы можете просто вернуть ссылку. отказ от ответственности, как указано выше.)

Если ваши объекты дешевы для копирования, вы также можете вернуть копию. (И это, по сути, по-прежнему можно передать адрес копии delete.)

+0

Слабые указатели - хорошее решение, если требуется семантика указателя, но, читая вопрос, я не совсем уверен, что это так. Многие новички просто используют указатели * везде *, а затем лучший ответ - просто «не». Но вы знаете это уже :) – jalf

+0

@jalf: _I_ знаете, но из принятого ответа похоже, что ваш комментарий был прав. ':)' – sbi

3

Ну, самый безопасный способ сделать это, чтобы вернуть копию объекта по значению:

MyObject GetMyObject() const {return _myObject;} 

Затем абонент может сделать все, что он хочет с его копией, и это не будет влиять на вас ,

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

Следующая лучшая вещь, чтобы вернуть константную ссылку на него:

const MyObject & GetMyObject() const {return _myObject;} 

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

Последнее, что вы могли бы сделать, это использовать подсчет ссылок для возврата ссылки на объект. В этом случае никто никогда не должен удалять объект, поскольку объект будет автоматически удален, когда последняя ссылка на него исчезнет. Изучите класс shared_ptr boost, чтобы узнать подробности об этом; Я настоятельно рекомендую подсчет ссылок как способ управления распределением памяти в C++, поскольку он автоматизирует прочь 99% потенциальных ошибок, которые программисты создают при управлении памятью.

+0

что? только возврат частной переменной означает, что я возвращаю копию? – Jonathan

+0

Да, это правильно - возврат по значению неявно копирует возвращаемый объект. –

3

В C++ вы ничего не можете сделать, чтобы предотвратить зависание вызывающей программы. Период.

Я бы предложил просто вернуть ссылку. Если вызывающий абонент отправляется и берет его адрес и передает его на номер delete, они просят о проблемах - и если они намерены разбить вещи, вы не можете их остановить, чтобы остановить их.

+0

отличный ответ. Я буду продолжать использовать ссылки ... Я думал, что ключевое слово const после функции() funciton сделает это возвратом undeletable или что-то вроде этого. Так что было бы лучше, потому что я использую много наследования и не могу использовать абстрактные классы с вектором, если не указатели (или как-то может i? = P) – Jonathan

+0

Это уже неуязвимо, так же как удаление &cout; является undeletable - то есть, если вы удалите его, все сломается, плохо :) Все, что вам нужно сделать, чтобы получить ту же магию, - это объявить в своей документации «НЕ СЛЕДУЕТ УДАЛИТЬ ЭТОТ СТУДИЮ» и избегать фактического возврата указателей, если это необходимо. И надеюсь, что у пользователя есть унция здравого смысла :) – bdonlan

0

Действительно специальный раствор может быть перегрузка оператора новые и удалять (и, возможно, новый []/удалить []), что делает удаление частный оператор и предоставление доступа только к классу владельца:

#include <memory> 

class Y 
{ 
private: 
    void* operator new(size_t size) { return new char[size]; } 
    void operator delete(void* p) { delete [] static_cast<char*>(p); } 
    friend class X; 
}; 

class X 
{ 
    Y* p; 
    X(const X& x); 
    const X& operator=(const X& x); 
public: 
    X(): p(new Y()) {} 
    ~X() { 
     delete p; //OK here, X is a friend 
    } 
    const Y* get() const { return p; } 
}; 

int main() 
{ 
    X x; 
    const Y* p = x.get(); 
    delete p; //error here: operator delete is private 
} 

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

+0

Хех, теперь это параноидальное решение;) – jalf

+0

вопрос: Могу ли я каким-то образом убедиться, что указатель обнулен после удаления? видя вашу перегрузку, я понял, что это можно сделать как-то. Может быть, если я могу перегрузить оператор ссылкой на указатель (* &). Хм ... – Jonathan

+0

Дословно то, о чем попросил ОП. Не то, что я особенно рекомендую. – UncleBens

0

Вы задумывались над тем, чтобы найти людей, которые пишут код, который удаляет переменные другого модуля, и спрей-роспись их puce или что-то в этом роде?

Серьезно, если ваши коллеги абсолютно настроены сломать вещи, вы действительно не можете с этим справиться (за исключением того, чтобы стрелять в них на месте, если вы босс). Черт возьми, нет гарантии, что то, что вы передаете, - это то, что было выделено отдельно, и если вызывающий абонент использует delete по случайному адресу, то не известно, что произойдет (за исключением того, что некоторая форма кучного повреждения почти наверняка).

В этом нет никакой конструкции языка программирования или языка программирования, что избавит вас от подобных коллег. «Против глупости сами боги тщетно борются».

+0

успокойтесь, успокойтесь ... Я учусь. Если вы считаете, что второй класс не может получить доступ к закрытым членам первого класса, почему не может быть ключевое слово, которое сообщает компилятору, что он не сможет удалить его из класса или его вида? искренне, все эти константы как префикс, sufix и midlefix O_o ... Я думал, что это будет так. – Jonathan

+0

Это не техническая проблема. Ключевое слово, которое вы хотите, конечно, является константой, но вы решили это в вопросе, предположительно потому, что какой-то идиот мог использовать const_cast <>() на нем и все равно удалять его, даже если его нельзя удалить. Если «const foo *» не работает, это происходит из-за того, что он намеренно подрывается, и это проблема людей. –

+1

@David: Вы можете «удалить» указатель на константу. Никакой бросок не требуется. – Troubadour

-1

Если у вас есть объект, который берет на себя (Банк берет учетную запись (изобретал, я знаю)).
Но люди хотят получить доступ к учетной записи, но вы не хотите, чтобы они ее удаляли.

class Bank 
{ 
    public: 
     // use auto_ptr to indicate transfer of ownership to the Bank. 
     void addAccount(std::auto_ptr<Account> a) 
     { 
      m_account = a; 
     } 
     // 
     // getAccount() returns a reference to the object 
     // Anbody using the object can NOT now delete it. 
     // But they can manipulate it. 
     Account& getAccount(int accountNo) 
     { 
      return *m_account; 
     } 
    private: 
     std::shared_ptr<Account> m_account; // In example bank only has 1 account 
} 
0

Я не уверен, если это то, что вы ищете, и это также зависит от того, сколько усилий вы действительно нужно принять, но вы всегда можете использовать pimpl idiom.

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

Например,

class Interface 
{ 
public: 
    Data getData() const { return Data(m_data); } 

private: 
    DataImpl* m_data; 
}; 

где

class Data 
{ 
public: 
    // Just use compiler-synthesised copy ctor, assignment and dtor 

    // Declare as many forwarding methods to impl as required 
    // ... 

private: 
    friend class Interface; 
    Data(DataImpl* d) : m_impl(d) {} 
    DataImpl*const m_impl; 
}; 

Простой пример здесь наивно, так как это не отменяет любые существующие Data ручки, когда DataImpl в конечном счете разрушен, и поэтому они остались с несвежим указатели. Это можно решить с большим трудом, но снова у вас остается вопрос, действительно ли это стоит того.