Re: Наследоваться или инкапсулировать?
От: vsb Казахстан  
Дата: 04.09.22 23:40
Оценка: +8
Здравствуйте, Marty, Вы писали:

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


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


Написать обычные функции string_func1, string_func2 и не городить классы.
Re: Наследоваться или инкапсулировать?
От: Went  
Дата: 05.09.22 10:33
Оценка: +5
Здравствуйте, Marty, Вы писали:
M>Хочу вот сделать свой класс строк, который предоставляет всё то, что даёт std::basic_string. Вот буквально пару методов добавить, при том, что всё новое будет работать через методы std::basic_string'а.
M>Отнаследоваться от std::basic_string'а, или сделать его членом класса, и написать/скопировать простыню делегирования?
M>В чем подводные камни?
Основной подводный камень обоих подходов — у тебя будет постоянно пропадать твой тип строки и вылазить std::string, который ты будешь вынужден опять (явно или неявно) приводить к твоему типу. Это тебя так задолбёт, что ты просто напишешь пару внешних функций, как советуют выше.
Re[3]: Наследоваться или инкапсулировать?
От: vsb Казахстан  
Дата: 05.09.22 07:26
Оценка: +4
Здравствуйте, Marty, Вы писали:

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


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


vsb>>Написать обычные функции string_func1, string_func2 и не городить классы.


M>Аргументы будут?


Аргументы в пользу гораздо более сложного решения должны быть у тебя. Пока я их не увидел, поэтому считаю, что ты хочешь все усложнить на ровном месте.
Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.09.22 23:12
Оценка: -1 :))
Здравствуйте!

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

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

В чем подводные камни?
Маньяк Робокряк колесит по городу
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 04.09.22 23:41
Оценка: -2 :)
Здравствуйте, vsb, Вы писали:

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


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


vsb>Написать обычные функции string_func1, string_func2 и не городить классы.


Аргументы будут?

ЗЫ Ну и от джависта интересно про C++ послушать
Маньяк Робокряк колесит по городу
Отредактировано 04.09.2022 23:43 Marty . Предыдущая версия .
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[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[7]: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 06.09.22 15:35
Оценка: :))
Здравствуйте, qaz77, Вы писали:

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

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

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


Зачем?
Перефразирую вопрос: где кроме embeded нужен SSO?
Перефразирую ещё раз: где в реальном мире используются short/small string так, чтобы необходимо было хранить их на стеке?
Ответ: ну практически нигде
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 скорость в несколько раз была выше на типичных операциях
Маньяк Робокряк колесит по городу
Re: Наследоваться или инкапсулировать?
От: reversecode google
Дата: 05.09.22 00:44
Оценка: -1
похвально что плюсы начали хотя бы читать
теперь вам нужно возвращаться в начало
и начинать изучать объектно ориентированное программирование
'has a' vs 'is a' — изучают в самом начале
Re: Наследоваться или инкапсулировать?
От: scf  
Дата: 05.09.22 09:41
Оценка: +1
Здравствуйте, Marty, Вы писали:

M>В чем подводные камни?


Вне зависимости от языка:
— наследование лучше использовать только для классов, спроектированных для наследования
— инкапсуляция (агрегация) интерфейсов предназначена для абстрагирования от интерфейса, а не для его расширения
— для расширения функциональности класса, если эта функциональность опирается на существующий интерфейс, используют extension functions — синтаксический сахар, позволяющий fun(a, b, c) вызывать в виде a.fun(b, c). В языках, куда их не завезли, обычные функции.
Re: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 05.09.22 11:19
Оценка: +1
У всех стандартных базовых классов есть ложечка дерьма — отсутствие виртуальных деструкторов. Потому и не часто от них любят наследоваться.
Re[7]: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 06.09.22 08:59
Оценка: -1
Здравствуйте, qaz77, Вы писали:

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


доказательства в студию. Компилятор видит указатель на std::string. Он знает адрес деструктора. Дальше просто конвертирует MyString& в std::string& и передаёт в деструктор. "Вижу цель, не вижу препятствий!"
Re[3]: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 06.09.22 11:55
Оценка: -1
Здравствуйте, qaz77, Вы писали:

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


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

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


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

В принципе уже давно назрела в сообществе потребность в нормальной (некоторые назовут её расширенной) стандартной библиотеке.
Как я вижу — нужно оставить старую библиотеку как V1 — будет использоваться всякими embeded и вирусописателями.
Но необходима новая библиотека/ки, которая покрывает все современные потребности для разработки.
Re: Наследоваться или инкапсулировать?
От: DiPaolo Россия  
Дата: 05.09.22 07:37
Оценка:
Я бы шел в такой последовательности:

1) функции-хелперы, как уже советовали выше
2) агрегация
3) наследование

Аргументы:
— логически, стринг — это базовый тип, который предоставляет все что нужно для работы со строками. То есть в каком-то смысле законченный объект. Все что сверху — пользовательские функции. Если нужно что-то с ними делать, то это можно решить утилитарными функциями
— в стринге как классе нет виртуальных функций. Отсюда делаем вывод, что разработчики не давали нам подсказку (читай, не закладывали такое поведение) наследоваться от него
— сам заголовочник содержит фразу
/** @file bits/basic_string.h
   26  *  This is an internal header file, included by other library headers.
   27  *  Do not attempt to use it directly. @headername{string}
   28  */

(https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html)
— класс стринг очень сложен и оптимизирован под быстродействие, равно как и заточен под широкий класс задач, а также спроектирован так, чтобы работать на всех платформах. Велик шанс что-то упустить во время реимплементации

Все это дает понять, что велик шанс отстрелить ногу.

И, на самом деле, первые вопросы, на которые надо ответить: а что вы собственно хотите сделать? И на каких платформах/компиляторах/версиях это должно поддерживаться? Одно дело — виндовое приложение, которое заточено под MSVC. Другое — СДК, которая запускается от MSVC до всевозможных экзотических платформ, где к тому же предоставляются кастомные аллокаторы, может использоваться wchar и т.п.
Патриот здравого смысла
Re: Наследоваться или инкапсулировать?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 05.09.22 08:38
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


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


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


M>В чем подводные камни?


Определи, что ты нарушишь из контракта базового класса, если отнаследуешься.
Если твёрдо уверен, что ничего — наследуйся.
Иначе — тщательно подумать и скорее всего отказаться.
The God is real, unless declared integer.
Re: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 05.09.22 09:07
Оценка:
Здравствуйте, Marty, Вы писали:

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


M>В чем подводные камни?


помню пытался такое провернуть для optional. Наткнулся на то что при "наследовании" конструкторов (using) не подхватываются некоторые правила вывода шаблонных параметров (надо их прописывать отдельно). Для string вроде такого быть не должно, но и конструкторов там куда больше — надо тестировать.
Делегирование — точно нет. Закопаетесь. Тогда кроме копирования функций придётся ещё следить за конвертацией из MyString в string и обратно.

В целом идея, как по мне, нормальная. От каждого следующего стандарта мы получаем по одной-двум функциям в строку. Зачем ждать условного С++50, если можно написать эти фукнции самому. Но может дождемся расширяющих методов и тогда упростят построение велосипедов.
Re[2]: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 05.09.22 09:27
Оценка:
попробовал, как минимум не работает конвертация из std::string

struct my_string: std::string
{
    using std::string::string;
};

int main() {
    const my_string mstr {"aaaaaaaaaaaaaaaaaaaaa"};
    const std::string sstr = mstr;
    const my_string mstr2 = sstr; // error
    return 0;
}


уже всё несколько непросто.
Re: Наследоваться или инкапсулировать?
От: B0FEE664  
Дата: 05.09.22 10:35
Оценка:
Здравствуйте, Marty, Вы писали:

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

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

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

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

M>В чем подводные камни?

Наследник или член класса, в любом случае
придётся написать 11 конструкторов (включая 3 шаблонных), 3*8+2 операторов и 13 внешних функций. И всё это ради двух методов?

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

Если же над вами давлеет идеология ООП и вам запрещено создавать внешние функции, то всегда есть способ сделать прокси класс (не проверял, просто набрал код):

namespace str_ex
{

class Ex
{
  public:
    Ex(std::string& str)
      : m_str(str)
    {
    }
  public:
    Ex& pop_front()
    {
      assert( ! m_str.empty());
      m_str.erase(m_str.begin());
      return *this;
    } 
  private:
    std::string& m_str;
};

}// namespace str_ex


using namespace str_ex;
....

std::string str("asdf");
std::cout << str;
Ex(str).pop_front().pop_front();
std::cout << str;
....
И каждый день — без права на ошибку...
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 10:36
Оценка:
Здравствуйте, DiPaolo, Вы писали:

DP>Аргументы:

DP>- логически, стринг — это базовый тип, который предоставляет все что нужно для работы со строками. То есть в каком-то смысле законченный объект. Все что сверху — пользовательские функции. Если нужно что-то с ними делать, то это можно решить утилитарными функциями

Предоставляет, да не всё. Мне не хватает


DP>- в стринге как классе нет виртуальных функций. Отсюда делаем вывод, что разработчики не давали нам подсказку (читай, не закладывали такое поведение) наследоваться от него


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


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


Я и не хочу ничего реимплементировать, а хочу расширить интерфейс, причем исключительно средствами уже существующего интерфейса


DP>И, на самом деле, первые вопросы, на которые надо ответить: а что вы собственно хотите сделать? И на каких платформах/компиляторах/версиях это должно поддерживаться? Одно дело — виндовое приложение, которое заточено под MSVC. Другое — СДК, которая запускается от MSVC до всевозможных экзотических платформ, где к тому же предоставляются кастомные аллокаторы, может использоваться wchar и т.п.


Ну, если про аллокаторы — то я шаблон хочу сделать, с теми же параметрами
Маньяк Робокряк колесит по городу
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 10:37
Оценка:
Здравствуйте, netch80, Вы писали:

N>Определи, что ты нарушишь из контракта базового класса, если отнаследуешься.

N>Если твёрдо уверен, что ничего — наследуйся.
N>Иначе — тщательно подумать и скорее всего отказаться.


А что я могу нарушить?
Маньяк Робокряк колесит по городу
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 10:43
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

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

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


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

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

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


BFE>
BFE>std::string str("asdf");
BFE>std::cout << str;
BFE>Ex(str).pop_front().pop_front();
BFE>std::cout << str;
BFE>....

BFE>


Лишняя писанина
Маньяк Робокряк колесит по городу
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.09.22 11:27
Оценка:
Здравствуйте, AeroSun, Вы писали:

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


У меня и у своих классов часто их нет. И что?
Маньяк Робокряк колесит по городу
Re[3]: Наследоваться или инкапсулировать?
От: B0FEE664  
Дата: 05.09.22 12:36
Оценка:
Здравствуйте, Marty, Вы писали:

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

BFE>>Что конкретно?
M>trim/split, например

Для таких методов подходит std::basic_string_view. Его, кстати, завернуть в свой класс намного проще.

M>Лишняя писанина

Писать обертку вокруг std::string — тоже лишняя писанина. Я не вижу ни одной причины наследоваться от std::string или же писать вокруг std::string обёртку. Разве это принципиально: писать имя функции слева или справа от переменной?:
split(str);
str.split();
И каждый день — без права на ошибку...
Re[2]: Наследоваться или инкапсулировать?
От: qaz77  
Дата: 05.09.22 13:44
Оценка:
Здравствуйте, AeroSun, Вы писали:


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


Я бы всем этим basic_string, vector и т.д. final бы прописал...
Любая виртуальная функция в т.ч. деструктор — это sizeof(void*) лишней памяти на инстанс.
Вот мне такой непродуктивный расход нафиг не нужен на таких мелких и везде употребимых классах.
Re[3]: Наследоваться или инкапсулировать?
От: sergii.p  
Дата: 05.09.22 14:18
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Я бы всем этим basic_string, vector и т.д. final бы прописал...

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


так причём здесь final и виртуальный деструктор? Если наследоваться от std::vector, таблицы виртуальных функций не создаётся, потому как нет этих самых виртуальных функций. В данном случае мы бесплатно получили функциональность вектора без накладных расходов (написания тонны кода).
Разговоры про виртуальный деструктор тоже мимо кассы. Ну не надо держать std::unique_ptr<std::vector>. Но никто вроде и не стремится к такому, когда наследуется от вектора.
Отредактировано 05.09.2022 18:43 sergii.p . Предыдущая версия .
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[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[6]: Наследоваться или инкапсулировать?
От: Videoman Россия https://hts.tv/
Дата: 06.09.22 15:10
Оценка:
Здравствуйте, AeroSun, Вы писали:

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

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


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

Ну тогда тебе в джаву или сишарп.
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[6]: Наследоваться или инкапсулировать?
От: Went  
Дата: 07.09.22 05:50
Оценка:
Здравствуйте, AeroSun, Вы писали:
AS>Вот к примеру из моего недавнего: делаю крипто библиотеку, нужен контейнер (вектор/дек/строка) который ведёт себя полностью как стандартный. Необходимое условие — при умирании объекта он обязан затереть свою память.
AS>Так вот в текущих плюсах с текущей стандартной библиотекой эта задача не решаема без костылей.
1. Кастомный аллокатор, который затирает память, которую высвобождает.
2. Кастомный delete/placement delete для самих объектов, который затирает память, которую высвобождает.
Re[8]: Наследоваться или инкапсулировать?
От: McQwerty Россия  
Дата: 08.09.22 13:10
Оценка:
Здравствуйте, AeroSun, Вы писали:
AS>Перефразирую вопрос: где кроме embeded нужен SSO?
SSO — это не только память, но и отсутствие обращения к куче. А доступ к ней синхронный из разных потоков. Больше обращений — меньше параллелизма.
Re[6]: Наследоваться или инкапсулировать?
От: B0FEE664  
Дата: 08.09.22 13:50
Оценка:
Здравствуйте, AeroSun, Вы писали:

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


Каждый элемент контейнера должен в деструкторе затирать свою память. Да, писанины много, но секьюрный байт и просто байт — это два разных типа.

ЗЫ А если включить режим паранойи, то вообще никакие данные не должны находится в памяти в расшифрованном виде — у каждого байта должен быть метод выдающий своё нешифрованное значение...
И каждый день — без права на ошибку...
Re[6]: Наследоваться или инкапсулировать?
От: Sm0ke Россия ksi
Дата: 08.09.22 20:16
Оценка:
Здравствуйте, AeroSun, Вы писали:

skip

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


skip

Тут же можно сделать свой аллокатор, который при освобождении затирает память.
Re: Наследоваться или инкапсулировать?
От: ksandro Мухосранск  
Дата: 16.09.22 11:43
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


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


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


M>В чем подводные камни?


Вообще конечно было бы неплохо узнать, что это за пара методов. Но как по мне, так однозначно надо делать пару свободных функций. Можешь засунуть их в какой-нибудь "namespace my_string_utils".

Почему не стоит делать свой класс строки. Просто потому что мы всё это лет 15 назад проходили. Когда-то каждый программист считал, что он просто обязан написать свою лучшую в мире реализацию строки. Мало того тогда еще все бибилиотеки и фреймворки имели свою реализацию строки. Помните AnsiString, CString, QString и др. Всех их вообще было довольно просто преобразовать друг в друга, но все равно приходилось очень много трахаться чтобы интегрировать разные части кода.
Потом вроде наконец то все стали использовать стандартный std::string или const char* (сейчас правда еще string_view появился, зоопарк растет), и все более менее довольны. Но многим по прежнему хочится написать свою новую молодежную реализацию класса строки. Главный подводный камень, что никому твой класс строки будет не нужен, и если кто-то будет юзать твой код, первое что он сделает, это попытается преобразовать твою строку во что-то стандартное, с чем он знает как работать. Даже если твоя строка будет всего лишь оберткой над стандартной, тебе все равно придется передавать ее в свои функции, чтоб использовасть новый функционал, то есть ты полностью привяжешь свой код к новой строке, либо ты весь код должен сделать шаблоном, что то еще удовольствие.

Мой тебе совет, не знанимайся фигней, сделай просто пару свободных функций, иначе потом будешь очень долго мучиться.
Отредактировано 16.09.2022 16:50 ksandro . Предыдущая версия .
Re[2]: Наследоваться или инкапсулировать?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.09.22 11:16
Оценка:
Здравствуйте, ksandro, Вы писали:

K>Вообще конечно было бы неплохо узнать, что это за пара методов.


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


K>Но как по мне, так однозначно надо делать пару свободных функций. Можешь засунуть их в какой-нибудь "namespace my_string_utils".


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


K>Почему не стоит делать свой класс строки. Просто потому что мы всё это лет 15 назад проходили. Когда-то каждый программист считал, что он просто обязан написать свою лучшую в мире реализацию строки. Мало того тогда еще все бибилиотеки и фреймворки имели свою реализацию строки. Помните AnsiString, CString, QString и др. Всех их вообще было довольно просто преобразовать друг в друга, но все равно приходилось очень много трахаться чтобы интегрировать разные части кода.


Дело не в преобразовании, а в том, что все эти AnsiString, CString, QString — они отличаются интерфейсом от std::string. Вот сейчас QString вроде бы привели к более или менее совместимому с std::string виду, и получается обычно писать код, который работает и с std::string и с QString, но, сцуко, они не сделали метод empty(), есть только isEmpty.


K>Потом вроде наконец то все стали использовать стандартный std::string или const char* (сейчас правда еще string_view появился, зоопарк растет), и все более менее довольны. Но многим по прежнему хочится написать свою новую молодежную реализацию класса строки. Главный подводный камень, что никому твой класс строки будет не нужен, и если кто-то будет юзать твой код, первое что он сделает, это попытается преобразовать твою строку во что-то стандартное, с чем он знает как работать. Даже если твоя строка будет всего лишь оберткой над стандартной, тебе все равно придется передавать ее в свои функции, чтоб использовасть новый функционал, то есть ты полностью привяжешь свой код к новой строке


Для своего кода — это не проблема


K>либо ты весь код должен сделать шаблоном, что то еще удовольствие.


А какие проблемы? У меня большая часть кода — шаблонная


K>Мой тебе совет, не знанимайся фигней, сделай просто пару свободных функций, иначе потом будешь очень долго мучиться.


У меня этих свободных функций уже 100500, и я уже долго мучаюсь
Маньяк Робокряк колесит по городу
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.