2016-06-15 12 views
0

Я думаю, что это вопрос с наилучшей практикой.C++ Модифицирование себя через пройденное задание

В C++ у меня есть класс-оболочка для пути файловой системы, аналогичного пути os.path Python. В этом классе-оболочке существует функция-член, называемая «Split», которая ищет последний разделитель пути и разделяет его на «хвост» (последняя часть пути) и «голова» (все остальное). В настоящее время функция использует свою собственную переменную-член m_filepath для разделения.

Некоторый код:

class FilePath { 
    public: 
     void Split(FilePath& head, FilePath& tail) const 
     { 
      FilePath h; 
      FilePath t; 
      //initialize h, t with portions of m_filepath... 
      head = h; 
      tail = t; 
     } 
    private: 
     std::string m_filepath; 
}; 

int main(int argc, char ** argv) 
{ 
    FilePath some_path_1("/"); 
    FilePath some_path_2("/home/"); 
    some_path_1.Split(some_path_1, some_path_2); 
    return 0; 
} 

Когда я сделать что-то вроде этого, m_filepath some_path_1 была бы быть перезаписаны любой «голова» оказался. Кажется, что и оператор const не обращает внимания.

Мой вопрос в том, как лучше всего справиться с этим? Выбросить исключение? Разрешить переписывание объекта (это меня беспокоит и звучит небезопасно) и сказать разработчикам быть осторожными? Сделать умное использование заявления о возврате?

+1

Я не думаю, что вам нужно что-либо сделать; если кто-то пишет 'some_path_1.Split (some_path_1, some_path_2);' тогда вы можете предположить, что им известно, что они перезаписывают старое содержимое 'some_path_1'. В манипуляции с трактом это не редкость делать такие вещи - извлекать часть пути на месте, сохраняет необходимость делать другую переменную. –

+0

Просто верните пару новых путей. В этом нет ничего особенного. Отключите выходные аргументы. Функция возвращает свой результат. –

ответ

0

Редактировать Ah! Я только понял, что вы хотели Split() сделать: сплит this в head и tail - и надеемся, что они не пройдут this в head или tail.

В другом сценарии, operator =(Class &rhs) лучшая практика является первым сравнить &rhs против this, чтобы избежать этой проблемы точно, - но я не думаю, что у вас есть, что проблема в любом случае. В том, что вы дали, не только ясно, что произойдет, ваша реализация достаточно хороша, чтобы это произошло. Я бы не волновался (так же, как сказал @MM).


Когда у вас есть функция, ожидание состоит в том, что функция будет работать на экземпляре класса: то есть, Split() отгородиться, или дать некоторую информацию о себе.

Что вы хотите - функция-член static: функция, которая, связанная с классом, не работает на предполагаемом this. Сделайте Split() функцию static:

static void Split(...); // Note no concept of const required - no `this` to modify! 

и назвать его так:

FilePath::Split(some_path_1, some_path_2); 

Или, вы можете сделать это так, что Split() возвращает хвост, и изменяет себя, чтобы быть только голова :

FilePath Split(); 

Но в этом случае это немного расплывчато, в каком направлении раскол происходит.Как насчет:

FilePath SplitHead(); 

FilePath SplitTail(); 

Те последние два могут быть реализованы в терминах метода static выше, например, так:

FilePath FilePath::SplitHead() { 
    FilePath head = *this; 
    Split(head, *this); 
    return head; 
} // FilePath::SplitHead() 

FilePath FilePath::SplitTail() { 
    FilePath tail; 
    Split(*this, tail); 
    return tail; 
} // FilePath::SplitTail() 
+0

Что касается вашего первого ответа, я в настоящее время не сохраняю переменную-член переменной m_filepath static, она уникальна для экземпляра FilePath и инициализируется тем, что отправляется с конструктором. Split использует m_filepath для его разбиения и хранения результата в голове и хвосте. Не приведет ли статическая функция-член к изменению дизайна? Извините, если я был расплывчатым с моим первоначальным вопросом, я обновил на всякий случай. – FlamboyantBacon

+0

Я не предлагаю, чтобы вы делали статичный 'm_filepath' - я понимаю, что он должен быть членом экземпляра' FilePath'. Я предложил сделать 'Split()' function 'static'. Это означает, что, как я описал, сама функция не привязана к какому-либо конкретному экземпляру - и все же все еще связана с операциями FilePath (и может иметь доступ к ее частным членам) –

+0

Я интерпретирую вопрос, говоря, что ' this-> m_filepath' используется в 'Split', поэтому он должен быть членом –

1

Большая проблема с написанием кода таким образом соблазн убрать код так что разделение выглядит следующим образом:

void Split(FilePath & h, FilePath & t) { 
    h.m_filepath = getHead(m_filepath); 
    t.m_filepath = getTail(m_filepath); 
} 

Но поскольку h.m_filepath = ... фактически меняется this.m_filepath второй вызов не делать то, что, как ожидается. (Обратите внимание, что ваш код в настоящее время отлично, но - хрупкий).

Корень проблемы (потенциала) представляет собой комбинацию использования ссылок в качестве возвращаемых значений и требующих нескольких возвращаемых значений. Но C++ 11 поддерживает несколько возвращаемых значений через tie.

Так что я бы осуществить это как

class FilePath { 
    public: 
     std::pair<FilePath,FilePath> Split() const 
     { 
      FilePath h; 
      FilePath t; 
      //initialize h, t with portions of m_filepath... 
      head = h; 
      tail = t; 
      return std::make_pair(h,t); 
     } 
    private: 
     std::string m_filepath; 
}; 

Тогда использование будет выглядеть следующим образом:

int main(int argc, char ** argv) 
{ 
    FilePath some_path_1("/"); 
    FilePath some_path_2("/home/"); 
    std::tie(some_path_1, some_path_2) = some_path_1.Split(); 
    return 0; 
} 

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

+0

Это может быть ответ, который я ищу. Я не знал, что C++ 11 допускает множественные возвращаемые значения. C++ 11 - это то, что я использовал (хотя это персональный проект, поэтому я готов обновить до 14, как только узнаю немного больше об этом). – FlamboyantBacon