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

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


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


Написать обычные функции string_func1, string_func2 и не городить классы.
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: Наследоваться или инкапсулировать?
От: reversecode google
Дата: 05.09.22 00:44
Оценка: -1
похвально что плюсы начали хотя бы читать
теперь вам нужно возвращаться в начало
и начинать изучать объектно ориентированное программирование
'has a' vs 'is a' — изучают в самом начале
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>Аргументы будут?


Аргументы в пользу гораздо более сложного решения должны быть у тебя. Пока я их не увидел, поэтому считаю, что ты хочешь все усложнить на ровном месте.
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: Наследоваться или инкапсулировать?
От: scf  
Дата: 05.09.22 09:41
Оценка: +1
Здравствуйте, Marty, Вы писали:

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


Вне зависимости от языка:
— наследование лучше использовать только для классов, спроектированных для наследования
— инкапсуляция (агрегация) интерфейсов предназначена для абстрагирования от интерфейса, а не для его расширения
— для расширения функциональности класса, если эта функциональность опирается на существующий интерфейс, используют extension functions — синтаксический сахар, позволяющий fun(a, b, c) вызывать в виде a.fun(b, c). В языках, куда их не завезли, обычные функции.
Re: Наследоваться или инкапсулировать?
От: Went  
Дата: 05.09.22 10:33
Оценка: +5
Здравствуйте, Marty, Вы писали:
M>Хочу вот сделать свой класс строк, который предоставляет всё то, что даёт std::basic_string. Вот буквально пару методов добавить, при том, что всё новое будет работать через методы std::basic_string'а.
M>Отнаследоваться от std::basic_string'а, или сделать его членом класса, и написать/скопировать простыню делегирования?
M>В чем подводные камни?
Основной подводный камень обоих подходов — у тебя будет постоянно пропадать твой тип строки и вылазить std::string, который ты будешь вынужден опять (явно или неявно) приводить к твоему типу. Это тебя так задолбёт, что ты просто напишешь пару внешних функций, как советуют выше.
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: Наследоваться или инкапсулировать?
От: AeroSun  
Дата: 05.09.22 11:19
Оценка: +1
У всех стандартных базовых классов есть ложечка дерьма — отсутствие виртуальных деструкторов. Потому и не часто от них любят наследоваться.
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 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.