Eugie » 15 ноя 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++ - вещи-то обсуждаем весьма серьезные. За вычетом филологических вопросов, конечно
[quote]
Я это и имел в виду. Передали кому-то CString и не учли новую ссылку. В многопоточной среде может случиться преждевременное обнуление счетчика.
[/quote]
Absurd, ты не мешай в одну кучу [i]внутренние ссылки класса CString[/i] (назовем их для отличия референциями) и [i]ссылки в смысле С++[/i] (далее С++-ссылки). При передаче куда-то ссылки на объект мы и не должны увеличивать счетчик референций, поскольку он предназначен для учета [i]копий[/i] данного CString-объекта, а не [i]С++-ссылок[/i] на него. А копию объекта мы в этом случае не создаем. Логика подсчета референций CString (ты сам ее описал) не конфликтует с возможностью объявить произвольное число С++-ссылок на CString. С++-ссылка сама не является объектом и не может пережить связанный с ней объект по определению, т.к. 1) должна быть обязательно инициализирована; 2) инициализирующий объект не может иметь область видимости меньшую, чем связанная с ним ссылка. Т.е. защита от дурака здесь реализована средствами самого языка.
В случае многопоточности ситуация сложнее. С одной стороны, - ты, наверное, знаешь - модификация референций в CString потокобезопасна. С другой - можно исхитриться и передать в поток ссылку на временный объект, который к моменту обращения к нему из потока будет разрушен. Но, 1) это не так просто сделать, и 2) это не есть специфика класса CString. Проблема будет в случае любого типа данных (см.пример), просто такое действие будет явно деструктивным. Любую защиту ведь можно поломать, было бы желание :)
Пример намеренного нарушения правил работы с ссылками в многопоточной среде:
[code]
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);
}
[/code]
PS. IMHO, надо бы вернуть топик в раздел C/C++ - вещи-то обсуждаем весьма серьезные. За вычетом филологических вопросов, конечно :)