[c++] кажется, я не понимаю как работает protected

elenangel

есть такой код, он не компилится (g++ 4.4.5)
 
 
#include <string>
#include <iostream>

using std::string;
using std::cout;
using std::endl;

class MutexStub
{
public:
virtual void lock{};
virtual void unlock{};
};

class StringWriter
{
private:
MutexStub mutex;
protected:
virtual void write(const string& s) = 0;
public:
void syncWrite(const string& s)
{
mutex.lock;
try
{
write(s);
mutex.unlock;
}
catch(...)
{
mutex.unlock;
throw;
}
}
};

class COutWriter : public StringWriter
{
protected:
void write(const string& s)
{
cout<<s<<endl;
}
};

class PreffixWriter : public StringWriter
{
protected:
void write(const string& s)
{
cout<<"# "<<s<<endl;
}
};

class DualWriter : public StringWriter
{
private:
StringWriter &first;
StringWriter &second;
protected:
void write(const string& s)
{
first.write(s);
second.write(s);
}
public:
DualWriter(StringWriter& f, StringWriter& s) : first(f second(s) {}
};

int main(int argc, char** argv)
{
COutWriter w1;
w1.syncWrite("COutWriter");
PreffixWriter w2;
w2.syncWrite("PreffixWriter");
DualWriter dw(w1,w2);
dw.syncWrite("DualWriter");
return 0;
}

ошибка возникает на вызовах first.write и second.write
разве нельзя вызвать защищенную (protected) функцию из потомка этого же класса?

apl13

разве нельзя вызвать защищенную (protected) функцию из потомка этого же класса?
Нет, разумеется, в этом и смысл этого слова.

evgen5555

разве нельзя вызвать защищенную (protected) функцию из потомка этого же класса?
Вызвать можно, через инстанциированный объект - нельзя.

elenangel

а почему тогда вот так можно:

class PreffixWriter : public COutWriter
{
protected:
void write(const string& s)
{

COutWriter::write("# "+s);
}
};

elenangel

блин, и что делать?

okis

Точно в этом? По-моему, как раз наоборот.
Т.е., из реализации метода можно вызывать, а извне — нет.

okis

Сделай их друзьями.

elenangel

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

evgen5555

минус, за спойлер!

Maurog

есть такой код, он не компилится (g++ 4.4.5)
у вас проблема в дизайне и язык (компилятор) вам это подсказывает.
ошибка возникает на вызовах first.write и second.write
и правильно делает.
разве нельзя вызвать защищенную (protected) функцию из потомка этого же класса?
вопрос неточен, поэтому отвечу прямо: так как вы написали делать нельзя
вот вероятно правильный ответ:
http://ideone.com/12Xor
ошибка дизайна: DualWriter наплевал на синхронизацию двух делегатов и пытался напрямую вывести строки
компилятор надавал а-та-та-та

Maurog

>> [c++] кажется, я не понимаю как работает protected
вы рассуждаете логично, но стандарт ввел лично для меня непонятное ограничение.

enochka1145

[зануда mode on]
Зачем точки с запятными здесь:
virtual void lock{};
virtual void unlock{};
?
Зачем вообще строчки
"private:"
и
"return 0;"
?
Почему в слове "PreffixWriter" всего 2 буквы "f"?
Почему в этом отрывке
StringWriter &second;
protected:
void write(const string& s)
один амперсанд слева, а другой - справа?

Maurog

[зануда mode on]
а меня больше всего забавляет имплементация метода syncWrite :grin:

zorin29

вот так перешел в режим зануды навсегда...

enochka1145

Я стараюсь делать БМВ, а не жигули. Отсюда удушающее занудство и нулевая толерантность к небрежностям.
Ну и потрындеть понятно, склонен.

enochka1145

Да уж, в XXI-м веке уже хочется, чтобы mutex-ы разблокировались автоматически в деструкторе.

pitrik2

"return 0;"
а чем это-то не угодило?
private: многие пишут типа для большей наглядности
всё остальное да, странно выглядит

Maurog

а чем это-то не угодило?
типа в main можно не писать return :grin:

rosali

а что плохого в том, чтобы сделать этот write публичным? ну с комментарием, что он не тредсейфный, ради бога. случаи разные бывают, иногда ты точно знаешь, что какой-то writer используется только в одном треде, иногда хочешь сделать 1000 write-ов подряд, и чтобы они все были под общим локом.
непубличным нужно делать то, что в будущем может исчезнуть или быть как-то совсем по-другому переписано, чтобы потом не искать все места, где заложились на старые предположения. а куда может исчезнуть метод write из класса с публичным методом syncWrite, или что можно в его семантике поменять, я не могу себе представить.
#define protected public
:cool:

elenangel

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

elenangel

[зануда mode on]

ну, слава Б-гу, а то я уж думал никто придираться не начнёт.

enochka1145

Если бы я придирался, я бы обратил внимание на непоследовательность в использовании пробелов и на то, что секции "private", "protected", "public" лучше расположить в обратном порядке.
Но тут у нас непонимание в простом базовом вопросе - "как работает protected", так что я решил особо не зверствовать.

okis

Почему, кстати? Если в c++ в классе по умолчанию права private, почему бы не начинать с них? С точки зрения синтаксиса более естественно. С другой стороны, для читателя декларации важнее увидеть сперва публичные и защищённые методы.

Serab

С другой стороны, для читателя декларации важнее увидеть сперва публичные и защищённые методы.
Именно. А про экономию слов ТС уже приводил тут фееричнейший пример: он не повторяет virtual в наследниках, но писал комментарии, что мол эта функция перекрывает определенную в базовом классе...

Serab

Если бы я придирался, я бы обратил внимание на непоследовательность в использовании пробелов
ты обратил, правда это еще не значит, что придирался :)

margadon

не повторяет virtual в наследниках, но писал комментарии, что мол эта функция перекрывает определенную в базовом классе
нда, уж лучше тупо писать virtual везде в наследниках

elenangel

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

Serab

так ты в этом треде не пишешь virtual все еще :)

elenangel

а зачем его писать?

enochka1145

// так ты в этом треде не пишешь virtual все еще
Ух ё-моё... :( :mad:
Да это вообще - расстрел на месте!

elenangel

вот если бы было ключевое слово означающее перекрытие виртуальной функции а-ля override я бы его писал. но его нету. а virtual я воспринимаю больше как ввод новой виртуальной функции и если в перекрытии его написать мне кажется это будет запутывать.

enochka1145

// а зачем его писать?
// // расстрел на месте
... с конфискацией имущества и без права реабилитации!

enochka1145

// перекрытие виртуальной функции а-ля override
Что это такое?
// но его нету
Нет - чего?

kill-still

//Что это такое?
переопределение метода
//Нет - чего?
хз как у вас в сях - по дефолту(без директивы) методы виртуальные?

elenangel

// перекрытие виртуальной функции а-ля override
Что это такое?
// но его нету
Нет - чего?

ты [/зануда mode on] сделай, а то на всю жизнь таким останешься

enochka1145

// хз как у вас в сях - по дефолту(без директивы) методы виртуальные?
Нет, конечно. Более того, даже виртуальные методы уважающий себя компилятор будет пытаться девиртуализировать. Ибо :) если процессор умеет предсказывать ветвления - неплохо бы этим пользоваться, а не бежать куда-то смотреть фактический адрес функии.

enochka1145

// ты [/зануда mode on] сделай, а то на всю жизнь таким останешься
Это нужно для дела. И уж лучше быть занудой, чем писать отстойный код для отстойных продуктов.
Мне вот за свою работу не стыдно.

elenangel

мне чисто эстетически не нравится слово virtual в потомке у переопределяемой функции. потому что в этом месте оно имеет другой смысл, чем в месте первого появления виртуальной функции.

Serab

#define override virtual :coquet: и то лучше :)

elenangel

#define впринципе зло

enochka1145

// мне чисто эстетически не нравится слово virtual в потомке у переопределяемой функции. потому что в этом месте оно имеет другой смысл, чем в месте первого появления виртуальной функции.
:shocked: Это какой же, позвольте узнать?

elenangel

class A
{
public:
virtual int f; // добавили в таблицу виртуальных функций класса новое вхождение
}
class D:public A
{
public:
virtual int f; // заменили указатель в таблице виртуальных функций на новый
}

enochka1145

class D:public A, public I
{
public:
// Inherited from A
virtual int f; // заменили указатель в таблице виртуальных функций на новый
// Inherited from I
virtual void g; // опять грязно проигнорировали разумные, добрые, вечные идеи C#
}

elenangel

а шарп тут причём?

enochka1145

Подозреваю, что override оттуда. Или из Java?.. Ну, где тебя насильно заставляют быть правильным и не дают права идти в ад собственной дорогой.

kill-still

шарп, делфи.
в яве ничего указывать не надо при переопределении.

elenangel

я с override знаком из Delphi и вроде бы в раннем C++ оно тоже было, хотя в этом я не уверен.

kill-still

а как в обозначениях си заслонить, или прервать VMT?

elenangel

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

kill-still

ни для чего хорошего, что нельзя бы было сделать без этого. :grin:

Dasar

честно говоря, не знаю. но и придумать, когда это надо было бы не могу навскидку.
версионность + децентрализованная разработка (когда предок и потомок модифицируются разными командами).
для B отнаследованного от A есть проблема в следующем сценарии:
сначала одна команда в B добавляет метод Problem
через некоторое время в A добавляется virtual метод Problem,
в итоге, B::Problem автоматически перекрывает метод A::Problem хотя это разные методы
изменить названия может быть проблемой, если уже есть куча стороннего кода.

kill-still

ммм а интерфейсы на что?

Dasar

ммм а интерфейсы на что?
накладно (и по времени разработки, и по скорости работы) на каждый чих делать по интерфейсу

ViperTM

Наследственный класс имеет доступ к защищенным методам и данным базового класса, но не к защищенным методам и данным инкапсулированного объекта, разве что только это не один и тот же класс. Разница между объектом и классом, по-моему, очевидна.

elenangel

Наследственный класс имеет доступ к защищенным методам и данным базового класса
вообще-то там доступ не к данным класса, а к данным экземпляра класса, на который ссылается this. Про то что this "равнее" других экземпляров - не очевидно.

Maurog

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

kill-still

кстати да. :)
З.Ы. а в сях есть такое исключение, что если предок и наследник объявлены в одном файле/юните(хз что там в сях то можно переопределять протектед?

Maurog

а в сях есть такое исключение, что если предок и наследник объявлены в одном файле/юните(хз что там в сях то можно переопределять протектед?
переопределять можно все, что является виртуальным. с протектед не связано никак. и с юнитами тоже
а в каком языке есть подобное "исключение" ?

elenangel

он фигово сформулировал. в Дельфях есть такая багофича, что все протектед члены класса, объявленные в пределах одного модуля видны всем классам внутри этого модуля, т.е. в пределах одного unit protected = public.

kill-still

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

Maurog

бля, короче жгу.
короче, нет ничего в C++ :grin:

elenangel

в c++ нету юнитов и их иногда не хватает, в основном из-за секций инициализации и деинициализации

kill-still

что-то нелапси куда-то пропал - упускает шикарную возможность меня подебнуть.
з.ы где на ведроид клаве твердый знак?:)

Maurog

в c++ нету юнитов
что вы подразумеваете под юнитами? в C++ есть translation unit, но вы явно что-то другое подразумеваете. в C++ нет модулей (пакетов) и это очень большой пробел, хотя он обусловлен спецификой самого языка

margadon

я на ойфоне помнится искал
если долго жать мягкий - вылезает твёрдый
может на ведроиде так же

Maurog

если долго жать мягкий - вылезает твёрдый
фцитаты :grin:

ViperTM

 first.write(s);  

это доступ к методу объекта (как ты выражаешься, экземпляру класса)
 ClassName::write(s);  

это доступ к методу класса
Класс — это абстракция, объект — память. Прошу понять.
DualWriter является наследником StringWriter, он имеет доступ к открытым и защищенным методам и данным базового класса (фактически, к своим). А first и second — это вставленные объекты. Ты имеешь право вызвать StringWriter::write, но не имеешь права вызвать first.write.
никакой здесь точности наоборот.
PS про this и равность экземпляров ничего не понял.

kill-still

юнит - это в некошерном языке делфи сущность, которая по сути является pas-файлом, а на самом деле некий более мелкий контейнер чем пакет (в делфи аналогом пакетов являются bpl, или как во всех виндах ocx, dll, exe) компилятор компилит их каждый отдельно в dcu, после чего уже компонует пакет. внутри него немного отличаются некоторые стандартные правила ооп. также импорт/инклюды в делфи идет не пакетами, а юнитами. импорты транзитивны, т.е. в файле проекта/пакета который сам тоже юнит и там, куда его импортнут, будет видно всё, из чего он состоит. как тут уже отметили, имеет блок (де)инициализации, который вызывается при при загрузке rt-инфы в память. как-то так.

kill-still

абстакция походу у вас в голове. :)

elenangel

ClassName::write(s); - если write не статический, и эта запись внутри метода класса-наследника ClassName то это тоже обращение к методу объекта this. Чем отличается класс от объекта я знаю не хуже тебя.

elenangel

как раз модули и подразумевал
Оставить комментарий
Имя или ник:
Комментарий: