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(). Достаточно сказать, что, если этот конструктор не используется в чрезвычайно узком цикле, затраты должны быть незначительными.

Другие вопросы по тегам