C++: Есть ли издержки проверки в конструкторе, если все данные уже могут быть действительными?
Я хочу создать исключение в своем конструкторе, чтобы мне не приходилось иметь дело с объектами зомби. Однако я также хочу предоставить метод проверки заранее, чтобы люди могли избежать "работы с исключениями" там, где для этого нет причин. В графическом интерфейсе не исключено, что недействительные данные. Однако я также хочу избежать дублирования кода и накладных расходов. Является ли компилятор GCC / Microsoft Visual C++ достаточно умным, чтобы устранить эту неэффективность проверки ввода дважды, и если нет, то есть ли небольшое изменение, которое могло бы порадовать?
Пример блока кода, иллюстрирующий мою точку зрения ниже:
#include <string>
#include <exception>
#include <iostream>
using std::string;
using std::cout;
using std::endl;
using std::exception;
// a validation function
bool InputIsValid(const string& input) {
return (input == "hello");
}
// a class that uses the validation code in a constructor
class MyObject {
public:
MyObject(string input) {
if (!InputIsValid(input)) //since all instances of input
throw exception(); //has already been validated
//does this code incur an overhead
//or get optimised out?
cout << input << endl;
};
};
int main() {
const string valid = "hello";
if (InputIsValid(valid)) {
MyObject obj_one(valid);
MyObject obj_two(valid);
}
return 0;
}
Я предполагаю, что это не может быть возможно, если объектный файл для класса генерируется один, поскольку объектный файл не может гарантировать, что люди будут проверять перед вызовом конструктора, но когда приложение компилируется и связывается вместе в одном приложении, можно пожалуйста?
2 ответа
Есть ли издержки проверки в конструкторе, если все данные уже могут быть действительными?
Да, если данные уже проверены, то вы бы понесли расходы на их повторную проверку
Является ли компилятор GCC / Microsoft Visual C++ достаточно умным, чтобы устранить эту неэффективность проверки ввода дважды, и если нет, то есть ли небольшое изменение, которое могло бы порадовать?
Вы можете инкапсулировать свой ввод в объект, и этот объект запомнит результат проверки.
template <typename INPUT_TYPE>
class InputObject {
INPUT_TYPE input_;
bool valid_;
public:
typedef <typename VALIDATE>
InputObject (INPUT_TYPE in, VALIDATE v) : input(in), valid_(v(in)) {}
const INPUT_TYPE & input () const { return input_; }
bool isValid () const { return valid_; }
};
typedef InputObject<std::string> MyInput;
class MyObject {
public:
MyObject (const MyInput &input) {
if (!input.isValid()) throw exception();
//...
}
};
Конструктор для InputObject
вызывает функцию проверки для вас и сохраняет результат проверки во флаге. затем MyObject
просто проверяет флаг, и не нужно делать проверку снова.
int main () {
MyInput input("hello", InputIsValid);
try {
MyObject obj_one(input);
MyObject obj_two(input);
}
catch (...) {
//...
}
}
Как полагает DeadMG, более строгая проверка типов может быть достигнута путем MyObject
принимайте только подтвержденные данные. Тогда бы не надо было throw
в конструкторе вообще. Такая схема может быть достигнута путем NonValidatedInput
а также ValidatedInput
два разных типа.
template <typename> class NonValidatedInput;
template <typename T>
class ValidatedInput {
friend class NonValidatedInput<T>;
T input;
ValidatedInput (const T &in) : input(in) {}
public:
operator const T & () const { return input; };
};
template <typename T>
class NonValidatedInput {
T input;
public:
operator ValidatedInput<T> () const { return ValidatedInput<T>(input); }
template <typename V>
NonValidatedInput (const T &in, V v) : input(in) {
if (v(input) == false) throw exception();
}
};
NonValidatedInput
принимает неподтвержденный ввод, выполняет проверку и может быть преобразован в ValidatedInput
объект, если проверка прошла успешно. Если проверка не пройдена, NonValidatedInput
бросает исключение. Таким образом, MyObject
вообще не нужно проверять правильность, потому что его конструктор принимает только ValidatedInput
,
typedef ValidatedInput<std::string> MyInput;
class MyObject {
public:
MyObject (MyInput input) {
std::string v = input;
std::cout << v << std::endl;
}
};
int main () {
try {
MyInput input = NonValidatedInput<std::string>("hello", InputIsValid);
MyObject obj_one(input);
MyObject obj_two(input);
}
catch (...) {
//...
}
}
Безопасность типов здесь довольно сильна, потому что только NonValidatedInput
может создать ValidatedInput
и только если проверка прошла успешно.
Оптимизатор может встроить функцию InputIsValid(), однако это далеко не все. Вы также должны задокументировать свой конструктор как бросающий, с типами исключений, которые он собирается выбросить. Что касается затрат производительности при вызове этой функции, то она незначительна, чем стоимость strcmp(). Достаточно сказать, что, если этот конструктор не используется в чрезвычайно узком цикле, затраты должны быть незначительными.