Re[3]: Наследоваться или инкапсулировать?
От: bnk СССР http://unmanagedvisio.com/
Дата: 05.09.22 19:57
Оценка:
Здравствуйте, Marty, Вы писали:

M>>>Хочу вот сделать свой класс строк, который предоставляет всё то, что даёт std::basic_string. Вот буквально пару методов добавить, при том, что всё новое будет работать через методы std::basic_string'а.

BFE>>Что конкретно?

M>trim/split, например


В других языках есть "extension methods" например, которые позволяют добавлять методы в класс без его (класса) изменения (то есть, добавить "снаружи").
Для С++ вроде бы это обсуждалось под названием "unified call syntax", но было забраковано по ряду причин.
Не вижу смысла идти против течения, я бы использовал обычные функции и не выделывался.

M>>>Отнаследоваться от std::basic_string'а, или сделать его членом класса, и написать/скопировать простыню делегирования?

BFE>>И всё это ради двух методов?

M>Пока два, да. Потом можно добавить по вкусу


Это "потом" никогда не наступит.
Re[4]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 20:29
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>В других языках есть "extension methods" например, которые позволяют добавлять методы в класс без его (класса) изменения (то есть, добавить "снаружи").


Угу, есть ещё Unified functions call (вроде бы так называется), где записи a.b(c) и b(a,c) — эквивалентны, но пока этого нет

bnk>Для С++ вроде бы это обсуждалось под названием "unified call syntax", но было забраковано по ряду причин.

bnk>Не вижу смысла идти против течения, я бы использовал обычные функции и не выделывался.

Если писать всегда с именем NS — то длинно. Если сделать using NS/using NS::name — можно получить ошибку компиляции и/или не тот вызов. А метод объекта — он всегда метод объекта


M>>Пока два, да. Потом можно добавить по вкусу


bnk>Это "потом" никогда не наступит.


Или да. Ещё starts_with/ends_with, например. Что-то такое вроде уже наконец вошло в 20ый стандарт, но что-то меня это не радует, по скорости принятия фич. Я вот хочу такую строку, где бы я мог дописывать свои методы, но база была бы написана за меня, пусть она SSO/COW или ещё как-то оптимизирована, меня не должно парить, я хочу, чтобы моя строка работала и как питоновская строка по интерфейсу, и как QString, и как что-то ещё, чтобы хотя бы мои наработки из питона или кутишного кода можно было бы собрать с минимумом перделок
Маньяк Робокряк колесит по городу
Re[5]: Наследоваться или инкапсулировать?
От: bnk СССР http://unmanagedvisio.com/
Дата: 05.09.22 20:32
Оценка:
Здравствуйте, Marty, Вы писали:

M>Или да. Ещё starts_with/ends_with, например. Что-то такое вроде уже наконец вошло в 20ый стандарт, но что-то меня это не радует, по скорости принятия фич. Я вот хочу такую строку, где бы я мог дописывать свои методы, но база была бы написана за меня, пусть она SSO/COW или ещё как-то оптимизирована, меня не должно парить, я хочу, чтобы моя строка работала и как питоновская строка по интерфейсу, и как QString, и как что-то ещё, чтобы хотя бы мои наработки из питона или кутишного кода можно было бы собрать с минимумом перделок


Выход есть — переходи на си шарп тайпскрипт
Отредактировано 05.09.2022 20:33 bnk . Предыдущая версия .
Re[6]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 20:59
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Выход есть — переходи на си шарп тайпскрипт


Там везде ограничений гораздо больше, чем в плюсиках, конкретно тайпскрипт, правда, не трогал, но вообще показалось — все тащат к себе фичи из плюсиков, это факт. Плюсики тоже, не спорю, сахарок из других языков к себе подвозят
Маньяк Робокряк колесит по городу
Re[4]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 07:57
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>так причём здесь final и виртуальный деструктор?


Я отвечал вот на это:
AS>У всех стандартных базовых классов есть ложечка дерьма — отсутствие виртуальных деструкторов. Потому и не часто от них любят наследоваться.

Соответственно, мое мнение, что виртуальный деструктор не нужен.
А финал нужен, что явно запретить наследование и не вводить в искус.

SP>Ну не надо держать std::unique_ptr<std::vector>. Но никто вроде и не стремится к такому, когда наследуется от вектора.


Обычно, уже есть код который работает с std::string или std::vector.
Потом делают какой-нибудь MyString или MyVector.
И проблема в рефакторинге существующего кода не только в виртуальном деструкторе.

Как минимум будет срезка типа при присваивании или передаче по значению.
auto s1 = MyString("abcd"); // ok
std::string s2 = MyString("abcd"); // срезка


Ну и с деструктором все места отловить будет сложно.
Только глазками. Компилятор все устраивает.
auto p1 = std::make_unique<MyString>("abcd"); // ok
std::unique_ptr<std::string> p2 = std::make_unique<MyString>("abcd"); // UB
Re[5]: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 06.09.22 08:40
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Соответственно, мое мнение, что виртуальный деструктор не нужен.

Q>А финал нужен, что явно запретить наследование и не вводить в искус.

ок, понял. С первым соглашусь, со вторым — категорически нет.

Q>Как минимум будет срезка типа при присваивании или передаче по значению.

Q>
Q>auto s1 = MyString("abcd"); // ok
Q>std::string s2 = MyString("abcd"); // срезка
Q>


здесь не будет срезки. MyString& конвертится в string& и передаётся в конструктор копирования.

Q>Ну и с деструктором все места отловить будет сложно.

Q>Только глазками. Компилятор все устраивает.
Q>
Q>auto p1 = std::make_unique<MyString>("abcd"); // ok
Q>std::unique_ptr<std::string> p2 = std::make_unique<MyString>("abcd"); // UB
Q>


такой код просто бессмысленен. Строка и вектор сами по себе указатели. Если такой код есть, это проблема программистов, даже не языка.
Ну и UB там тоже не будет. unique_ptr<MyString> прекрасно сконвертится в p2. Да и удаление пройдёт удачно. Вызовется деструктор std::string без ~MyString. Но если MyString — просто обёртка без данных, то всё ок.
В общем даже если хотеть навредить, это сделать очень сложно
Re[3]: Наследоваться или инкапсулировать?
От: Teolog  
Дата: 06.09.22 08:41
Оценка:
M>У меня и у своих классов часто их нет. И что?

Удаление по указателю на базовый класс сделает больно и непонятно, а это не только обычный но и умные указатели.
В общем класс от которого наследоваться не следует ибо мина.
Re[6]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 08:43
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Да и удаление пройдёт удачно. Вызовется деструктор std::string без ~MyString. Но если MyString — просто обёртка без данных, то всё ок.

Вот это UB по стандарту, если нет виртуального деструктора.
Re[7]: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 06.09.22 08:59
Оценка: -1
Здравствуйте, qaz77, Вы писали:

Q>Вот это UB по стандарту, если нет виртуального деструктора.


доказательства в студию. Компилятор видит указатель на std::string. Он знает адрес деструктора. Дальше просто конвертирует MyString& в std::string& и передаёт в деструктор. "Вижу цель, не вижу препятствий!"
Re[8]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 09:03
Оценка: 1 (1) +1
Здравствуйте, sergii.p, Вы писали:
SP>доказательства в студию. Компилятор видит указатель на std::string. Он знает адрес деструктора. Дальше просто конвертирует MyString& в std::string& и передаёт в деструктор. "Вижу цель, не вижу препятствий!"

C++ standard 5.3.5/3:

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
Re[3]: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 06.09.22 11:55
Оценка: -1
Здравствуйте, qaz77, Вы писали:

Q>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс.


Для контейнерных типов это несущественно

Q>Вот мне такой непродуктивный расход нафиг не нужен на таких мелких и везде употребимых классах.


Тут ключевое слово "тебе". А многим людям нужно. И ещё вопрос кого больше — кому "не нужно" или кому "нужно"?

В принципе уже давно назрела в сообществе потребность в нормальной (некоторые назовут её расширенной) стандартной библиотеке.
Как я вижу — нужно оставить старую библиотеку как V1 — будет использоваться всякими embeded и вирусописателями.
Но необходима новая библиотека/ки, которая покрывает все современные потребности для разработки.
Re[5]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.09.22 12:11
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Ну и с деструктором все места отловить будет сложно.

Q>Только глазками. Компилятор все устраивает.
Q>
Q>std::unique_ptr<std::string> p2 = std::make_unique<MyString>("abcd"); // UB
Q>


А это точно скомпилируется?
Маньяк Робокряк колесит по городу
Re[4]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 12:26
Оценка:
Здравствуйте, AeroSun, Вы писали:

Q>>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс.

AS>Для контейнерных типов это несущественно
Такой подход противоречит плюсовой идиоме про "не платишь за то, что не используешь".

Вектор обычно размером в 3 указателя, а тут 4-й подъехал, sizeof на 25% больше.
А для строк тем более расход чувствителен, причем эту часть sizeof SSO не сможет использовать.

AS>В принципе уже давно назрела в сообществе потребность в нормальной (некоторые назовут её расширенной) стандартной библиотеке.

AS>Как я вижу — нужно оставить старую библиотеку как V1 — будет использоваться всякими embeded и вирусописателями.
AS>Но необходима новая библиотека/ки, которая покрывает все современные потребности для разработки.

Уже было такое, когда все подряд наследуется от одного предка с виртуальным деструктором.
У Borland был TObject основной смысл которого в полиморфном удалении любого наследника.
Такой тотальный ООП впоследствии вызывал немало критики...

По моему мнению, в стандартной библиотеке C++ наследование уместно только для std::exception и его стандартных наследников.
Сам принцип работы catch нам навязывает наследование.

А для других классов STL — это дичь и разбрасывание граблей.
Re[6]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 12:35
Оценка:
Здравствуйте, Marty, Вы писали:

Q>>
Q>>std::unique_ptr<std::string> p2 = std::make_unique<MyString>("abcd"); // UB
Q>>


M>А это точно скомпилируется?


При условии, что MyString public наследник std::string скомпилируется.
Стандартный подход при реализации фабрик, когда на выходе unique_ptr для интерфейса, а make_unique для реализаций.
Re[5]: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 06.09.22 14:17
Оценка: :))
Здравствуйте, qaz77, Вы писали:

Q>Здравствуйте, AeroSun, Вы писали:


Q>>>Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс.

AS>>Для контейнерных типов это несущественно
Q>Такой подход противоречит плюсовой идиоме про "не платишь за то, что не используешь".

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

Q>Вектор обычно размером в 3 указателя, а тут 4-й подъехал, sizeof на 25% больше.


Для контейнерных типов — это ни о чём, микроскопическое изменение по сравнению с обычным его размером.

Q>А для строк тем более расход чувствителен, причем эту часть sizeof SSO не сможет использовать.


Для строк — это тоже ни о чём, SSO не нужен.

Ещё раз, про embeded и вирусописателей я не говорю — им можно оставить старую версию библиотеки.

Q>Уже было такое, когда все подряд наследуется от одного предка с виртуальным деструктором.

Q>У Borland был TObject основной смысл которого в полиморфном удалении любого наследника.
Q>Такой тотальный ООП впоследствии вызывал немало критики...

При чём тут тотальный ООП? Мы о другом

Q>По моему мнению, в стандартной библиотеке C++ наследование уместно только для std::exception и его стандартных наследников.

Q>Сам принцип работы catch нам навязывает наследование.

Q>А для других классов STL — это дичь и разбрасывание граблей.


Тут ты не прав. Есть куча случаев когда это нужно.

Вот к примеру из моего недавнего: делаю крипто библиотеку, нужен контейнер (вектор/дек/строка) который ведёт себя полностью как стандартный. Необходимое условие — при умирании объекта он обязан затереть свою память.
Так вот в текущих плюсах с текущей стандартной библиотекой эта задача не решаема без костылей.

Да, я на всяк случай прошерстил все крипто библиотеки и вот такие результаты в зависимости от библиотеки:
1) Вручную вызывают затирание перед уничтожением объекта. Это костыль — так как легко забыть это сделать
2) Реализовывают свой STL-совместимый контейнер. И я скажу это не тривиальная задача. Это костыль — стандартные средства не помогают
3) Делают свой объект в который включают вектор. Экспозят либо часть интерфейса либо полный интерфейс стандартного контейнера. Это костыль по очевидным причинам.

А всего-то нужно было отнаследоваться и переопределить деструктор.
При таких недостатках — то же SSO ну не интересен вообще никак.
Re[6]: Наследоваться или инкапсулировать?
От: Videoman Россия https://hts.tv/
Дата: 06.09.22 15:10
Оценка:
Здравствуйте, AeroSun, Вы писали:

Если такие специфические требования то нужно писать свой контейнер. Заодно на уровне типов защитишься от того, что кто-то передаст его как вектор и мувнет куда-нибудь.

AS>А всего-то нужно было отнаследоваться и переопределить деструктор.


И что? При росте размера вектора у тебя по всей памяти будут расползаться копии твоих секретных данных при переаллокации буфера Нафиг такой говнокод.
Re[6]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 15:25
Оценка:
Здравствуйте, AeroSun, Вы писали:
AS>Для строк — это тоже ни о чём, SSO не нужен.

Ну тогда тебе в джаву или сишарп.
Re[7]: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 06.09.22 15:35
Оценка: :))
Здравствуйте, qaz77, Вы писали:

Q>Здравствуйте, AeroSun, Вы писали:

AS>>Для строк — это тоже ни о чём, SSO не нужен.

Q>Ну тогда тебе в джаву или сишарп.


Зачем?
Перефразирую вопрос: где кроме embeded нужен SSO?
Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке?
Ответ: ну практически нигде
Re[8]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 06.09.22 15:41
Оценка:
Здравствуйте, AeroSun, Вы писали:

AS>Зачем?

AS>Перефразирую вопрос: где кроме embeded нужен SSO?
AS>Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке?
AS>Ответ: ну практически нигде

struct Person
{
  std::string f;
  std::string i;
  std::string o;
};

void foo(const Person& some_person)
{
   Person copy_of_person = some_person; // только одна дорогая аллокация для копирования длинного отчества
   // do something with copy_of_person...
}

int main()
{
  Person p;
  p.f = "Иванов";        // SSO works!
  p.i = "Иван";          // SSO works!
  p.o = "Владиславович"; // SSO don't works(( 
  foo(p);
  return 0;
}
Re[8]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.09.22 16:49
Оценка: 1 (1)
Здравствуйте, AeroSun, Вы писали:

AS>Зачем?

AS>Перефразирую вопрос: где кроме embeded нужен SSO?
AS>Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке?
AS>Ответ: ну практически нигде

Я, например, делал Decimal, который хранит десятичные цифры числа. В качестве хранилища использовал vector/string, так вот при использовании string скорость в несколько раз была выше на типичных операциях
Маньяк Робокряк колесит по городу
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.