Правильно ли записана перегрузка оператора?
Да, но можно добиться большего. Как кто-то еще упомянул, ваша функция может быть полностью определена из существующих, открытых функций. Почему бы не использовать его только те? Прямо сейчас это друг, а это значит, что он относится к деталям реализации. То же самое верно, если вы поместите operator << в качестве члена в ваш класс. Тем не менее, сделайте так, чтобы ваш оператор << a <em>не являлся членом , не являлся другом функция.
class ExtendedVector {
...
};
// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
cont.AddChar(str);
return cont;
}
Если вы измените свой класс, вы не будете уверены, что ваш оператор << все еще будет работать. Но если ваш оператор << полностью зависит только от открытых функций, то вы можете быть уверены, что он будет работать после того, как были внесены изменения только в детали реализации вашего класса. Ура! </p>
Является ли хорошей практикой перегрузка операторов в подобных ситуациях?
Как снова сказал другой парень, это спорно. Во многих ситуациях перегрузка операторов на первый взгляд будет выглядеть «аккуратно», но в следующем году будет выглядеть адом, потому что вы больше не имеете ни малейшего представления о том, что вы имели в виду, когда наделяли некоторые символы особой любовью. В случае оператора <<, я думаю, что это нормально использовать. Его использование в качестве оператора вставки для потоков хорошо известно. И я знаю о приложениях Qt и KDE, которые широко используют его в таких случаях, как </p>
QStringList items;
items << "item1" << "item2";
Аналогичным случаем является boost.format
, который также использует operator%
для передачи аргументов для заполнителей в своей строке:
format("hello %1%, i'm %2% y'old") % "benny" % 21
Конечно, также можно использовать его там. Но его использование для спецификаций формата printf хорошо известно, и поэтому его использование тоже хорошо, imho. Но, как всегда, стиль тоже субъективен, поэтому возьмите его с крошкой соли:)
Как я могу принимать аргументы переменной длины безопасным способом?
Ну, есть способ принять вектор, если вы ищете однородные аргументы:
void AddChars(std::vector<std::string> const& v) {
std::vector<std::string>::const_iterator cit =
v.begin();
for(;cit != v.begin(); ++cit) {
AddChar(*cit);
}
}
Это не очень удобно, чтобы пройти это все же. Вы должны создать свой вектор вручную, а затем передать ... Я вижу, у вас уже есть правильное представление о функциях стиля vararg. Не следует использовать их для такого рода кода и только при взаимодействии с кодом C или функциями отладки, если вообще. Другой способ справиться с этим случаем - применить программирование препроцессора. Это сложная тема и довольно хакерская. Идея состоит в том, чтобы автоматически генерировать перегрузки до некоторого верхнего предела, примерно так:
#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
/* now access arg0 ... arg(X-1) */ \
/* AddChar(arg0); ... AddChar(arg(N-1)); */ \
GEN_PRINT_ARG1(X, AddChar, arg) \
}
/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)
Это псевдокод. Вы можете посмотреть библиотеку препроцессора boost здесь .
Следующая версия C ++ предложит гораздо лучшие возможности. Можно использовать списки инициализаторов:
void AddChars(initializer_list<std::string> ilist) {
// range based for loop
for(std::string const& s : ilist) {
AddChar(s);
}
}
...
AddChars({"hello", "you", "this is fun"});
В следующем C ++ также возможно поддерживать произвольное множество аргументов (смешанного типа) с использованием шаблонов с переменными числами . GCC4.4 будет поддерживать их. GCC 4.3 уже частично их поддерживает.