PDA

Просмотр полной версии : Про ссылки и указатели (продолжение)



Absurd
08.11.2004, 13:53
DeeJayC прав, COut надо передавать по ссылке.
Может оказаться так что одна или несколько ссылок на StringBuffer будут неучтены, поскольку конструктор CString не отработает.
Тогда StringBuffer будет удален раньше времени и закончится это Protection fault'ом.

Программист всегда предпочитает пользоваться теми средствами, которыми владеет лучше
Это да. Нормальными средствами тяжело владеть.

Любое более-менее сложное решение подразумевает состыковку. Это нормальная часть работы программиста, работающего в команде.
Сейчас заставляю работать один модуль, написанный каким-то Шакилой, который в качестве параметра берет не List, а Vector, вместо потока - имя файла итп. Требует пермишены на запись для своих конфигов, которые он только читает и не модифицирует.
Какой бы это ужас был, если бы это было не на Яве, а на С++ :()

Насчет мудаков и исключений - не вижу корреляции.
В C++ нету исключений. Использовать то, чего нет - это мудачество.
Нету ключевого слова finally, нету уборки мусора.
Я не говорю, что нужно делать уборку мусора обязачельно - достаточно по дефолту делать,
скажем, подсчет ссылок, и включить в библиотеку хедер <gc.h>,
который можно было бы использовать опционально.
auto_ptr - это вообще чудо. Он меняет свое содержимое при присваивании.
Такой ужас надо еще постараться придумать.
Откуда пошло слово мудак:
Обычно монголы кастрировали русских мужчин.
Но для некоторых это была большая трагедия, и монголам было их жалко.
Поэтому им усекали спермоносные каналы, и количество мужских гормонов не менялось.
Трахаться они могли, могли эякулировать чистым предстательным соком,но детей они
иметь не могли.
Отсюда пошло слово "мудак" - то есть вроде бы при мудях, а вроде бы и нет.

Eugie
09.11.2004, 17:32
Может оказаться так что одна или несколько ссылок на StringBuffer будут неучтены, поскольку конструктор CString не отработает.
Тогда StringBuffer будет удален раньше времени и закончится это Protection fault'ом.


Какие ссылки, где учтены? В огороде бузина...;) Absurd, уверяю тебя: передача параметра по ссылке вполне безопасна и по сути эквивалентна передаче по указателю. Если возражаешь, плиз приведи конкретный пример, а не общие слова.



В C++ нету исключений. Использовать то, чего нет - это мудачество.


В C++ есть исключения (см. throw, try, catch). Другое дело, что C++ модель exception handling низкоуровневая - все надо делать самому. Да, это неудобно, и, главное, трудоемко. Конечно, приятнее пользоваться развитой иерархией исключений какой-нить среды типа .NET



Нету ключевого слова finally, нету уборки мусора.


C++ такой язык - предоставляет минимум готовых решений, но при этом позволяет реализовать практически все самому. Согласен, что с finally и GC удобнее, особенно когда привык. К хорошему вообще быстро привыкаешь :)

PS. Спасибо, что просветил по вопросу этимологии :) М***ки, в сущности, вызывают сочувствие как невинные жертвы татаро-монгольского ига :)))

Anonymous
10.11.2004, 14:23
Какие ссылки, где учтены? В огороде бузина... Absurd, уверяю тебя: передача параметра по ссылке вполне безопасна и по сути эквивалентна передаче по указателю. Если возражаешь, плиз приведи конкретный пример, а не общие слова.


В принципе, сам класс CString содержит только одно поле размером 32 бита
- указатель TCHAR* m_pStr.
и CString::operator const TCHAR*() реализован примерно так:



CString::operator const TCHAR*() {
return m_pStr;
}


Так где же находятся остальные данные CString'а ?
А они находятся перед строкой.
Строка "Hello world" выглядит так




[CString::m_pStr]
|
+---------------------------------------------+
|
[начало выделенной памяти |Разные данные | Cчетчик ссылок |Hello world\0



CString избегает ненужного копирования буффера.Буффер копируется только, если строка изменена.
В Java используется развитие этого метода - там вообще все строки неизменяемые.
Для изъятия фрагмента используется String.substring, который порождает новый (неизменяемый) объект.
Поскольку CString избегает копирования, несколько CString::m_pStr могут ссылаться на один
StringBuffer.
После кода


CString a = _T("Hello world");
CString b = a;
CString c = b;


Вся конструкция будет выглядеть так



[Объект a. CString::m_pStr]
|
+--------------------------------------+
[Объект b. CString::m_pStr] |
| |
+--------------------------------------+
[Объект c. CString::m_pStr] |
| |
+--------------------------------------+
|
[начало выделенной памяти |Разные данные |Cчетчик ссылок = 3|Hello world\0



Соответственно, StringBuffer удаляется, когда счетчик ссылок становится равным нулю.
Если кто-то ссылается на объект a, b или c через ссылку, тогда получается что
На строку Hello world ссылаются четыре или больше раз, а счетчик ссылок равен 3.
И он может быть удален раньше времнеи.
В принципе, на CString не надо и при помощи указателя ссылаться, так так CString - это
по сути и есть разновидность смарт - указателя.


Согласен, что с finally и GC удобнее, особенно когда привык. К хорошему вообще быстро привыкаешь

finally Страуструп не внес из-за своих религиозных убеждений.

Eugie
10.11.2004, 17:04
Если кто-то ссылается на объект a, b или c через ссылку, тогда получается что
На строку Hello world ссылаются четыре или больше раз, а счетчик ссылок равен 3.

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

AiK
10.11.2004, 17:19
Absurd, ты зачем шифруешься? :)

Absurd
12.11.2004, 13:48
Внутренний счетчик ссылок на строку не инкрементируется при передаче параметра по ссылке.
Я это и имел в виду. Передали кому-то CString и не учли новую ссылку. В многопоточной среде может случиться преждевременное обнуление счетчика.

Absurd, ты зачем шифруешься?
Глюк форума

Eugie
15.11.2004, 12:12
Я это и имел в виду. Передали кому-то CString и не учли новую ссылку. В многопоточной среде может случиться преждевременное обнуление счетчика.


Absurd, ты не мешай в одну кучу внутренние ссылки класса CString (назовем их для отличия референциями) и ссылки в смысле С++ (далее С++-ссылки). При передаче куда-то ссылки на объект мы и не должны увеличивать счетчик референций, поскольку он предназначен для учета копий данного CString-объекта, а не С++-ссылок на него. А копию объекта мы в этом случае не создаем. Логика подсчета референций CString (ты сам ее описал) не конфликтует с возможностью объявить произвольное число С++-ссылок на CString. С++-ссылка сама не является объектом и не может пережить связанный с ней объект по определению, т.к. 1) должна быть обязательно инициализирована; 2) инициализирующий объект не может иметь область видимости меньшую, чем связанная с ним ссылка. Т.е. защита от дурака здесь реализована средствами самого языка.

В случае многопоточности ситуация сложнее. С одной стороны, - ты, наверное, знаешь - модификация референций в CString потокобезопасна. С другой - можно исхитриться и передать в поток ссылку на временный объект, который к моменту обращения к нему из потока будет разрушен. Но, 1) это не так просто сделать, и 2) это не есть специфика класса CString. Проблема будет в случае любого типа данных (см.пример), просто такое действие будет явно деструктивным. Любую защиту ведь можно поломать, было бы желание :)

Пример намеренного нарушения правил работы с ссылками в многопоточной среде:


class A {
int& m_r;
public:
A(int& r): m_r(r) {}
int GetByRef() { return m_r; }
};

unsigned long thrd_hdl;
unsigned thrd_addr;

unsigned __stdcall thread_routine(void *p)
{
A* pa = reinterpret_cast<A*>(p);
cout << pa->GetByRef() << endl;
return 0;
}

void f()
{
// Передаем в создаваемый поток ссылку на временную переменную типа int
// (через указатель на статичесий объект, т.к.напрямую нельзя). К моменту
// вызова thread_routine ссылка становится инвалидной.
// А если объявить static int n - все будет OK.
int n = 1;
static A a(n);

thrd_hdl = _beginthreadex(
NULL,
0,
thread_routine,
(void *)&a,
0,
&thrd_addr);
}

void main()
{
f();
Sleep(1000L);
}


PS. IMHO, надо бы вернуть топик в раздел C/C++ - вещи-то обсуждаем весьма серьезные. За вычетом филологических вопросов, конечно :)

Absurd
17.11.2004, 20:13
При передаче куда-то ссылки на объект мы и не должны увеличивать счетчик референций, поскольку он предназначен для учета копий данного CString-объекта, а не С++-ссылок на него.
Какая разница? Все равно это нарушение политики, которую предстравляет смарт-указатель. В С++ есть соглашение (contract), что смарт - указатели должны пользоваться конструктором копирования и operator=() для отслеживания жизни указуемого объекта.
Если мы пользуем ссылку на смарт - указатель, то мы обходим его политику.

Eugie
18.11.2004, 17:49
CString - не смарт-указатель, хотя тоже умеет считать ссылки.
Absurd, приведи мне плиз конкретный пример кода, где передача CString в функцию по ссылке небезопасна. Иначе спорить можно еще долго, а время - деньги :)

Absurd
22.11.2004, 13:10
Absurd, приведи мне плиз конкретный пример кода, где передача CString в функцию по ссылке небезопасна
Указатель - это наиболее общий способ передачи ссылки на объект. Ссылки вторичны и служат инструментом для написания базовых примитивов в программе.
Значит, надо пользоваться указателями для соблюдения порядка в API.

Eugie
22.11.2004, 13:27
Ссылки - такой же элемент синтаксиса C++, как и указатели. Просто они удобнее :)
Ладно, Absurd, предлагаю на этом закончить спор как себя исчерпавший. Если будет желание продолжить - плиз, в частном порядке.