2009-12-23 2 views
3

Все классы, над которыми я работаю, имеют методы Create()/Destroy() (или Initialize()/Finalized()).Как я могу проверить сбой в constructor() без использования исключений?

Возвращаемое значение метода Create(): bool, как показано ниже.

bool MyClass::Create(...); 

Поэтому я могу проверить, успешна ли инициализация экземпляра или нет из возвращаемого значения.

Без Create()/Destroy() Я могу выполнить одно и то же задание в конструкторе() и destructor(), но я не могу решить ниже проблему.

Может ли кто-нибудь мне помочь? Заранее спасибо.

Я не могу использовать исключения, потому что моей компании это не нравится.

class Foo 
{ 
private: 
    AnotherClass a; 
public: 
    Foo() 
    { 
     if(a.Initialize() == false) 
     { 
      //??? 
      //Can I notify the failure to the user of this class without using exception? 
     } 
    } 
    ... 
}; 

Foo obj; 
+3

Означает ли ваша компания, что исключения присутствуют каждый раз, когда они называются «новыми», или в любое время, когда они используют стандартную библиотеку? – GManNickG

+0

@GMan Мы склонны переопределять оператор new. и я не знаю о проблемах с библиотекой, не могли бы вы рассказать мне немного больше? – sevity

+0

Используйте ключевое слово nothrow в этом случае, чтобы избежать исключения. int * p = new (nothrow) int [100]; // on fail возвращает указатель NULL Но как вы избежите исключения bad_cast, может быть, вам нужно отключить исключение? – Ashish

ответ

6

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

  1. Конструктор принимает ссылку/указатель на параметр, который сообщит статус ошибки для вызывающего абонента.
  2. Класс реализует метод, который возвращает статус ошибки конструктора. Ответчик за проверку этого метода будет отвечать.

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

2

Почему исключения не должны использоваться? Лучший способ вернуть ошибку из конструктора - это выбросить исключение. Объект, который построен неправильно, не должен использоваться, и исключение throwing гарантирует это.

Вы можете передать этот FAQ: How can I handle a constructor that fails?

+1

как sevity написал: политика компаний исключает исключения. – 2009-12-23 07:00:28

0

два предложения:

  • старомодный setjmp/longjmp()
  • глобальной ERRNO так переменной
3

Существует не Хороший путь; это одна из основных причин, по которой они были добавлены в язык в первую очередь. Без исключений:

  1. Выполнение определенного рода assert() в конструкторе для остановки выполнения; невозможно игнорировать, но невозможно оправиться.
  2. Сделайте свою фактическую конструкцию в функции Init.
  3. ... или сохранить его внутри конструктора, но установить «плохой» флаг.

Лично я считаю, что 2 строго лучше, чем 3, так как он не увеличивает размер класса и делает его более заметным, когда функция «проверка» не вызывается. Есть причины, о которых я процитировал, например, вы можете получить доступ к виртуальным функциям, но я всегда считал это довольно слабым.

+0

Но не точка (2) и (3) по существу то же самое? Вы оба требуете «плохой» флаг, чтобы последующие вызовы методов могли проверяться. Я бы сказал, что в пункте (2) вы минимизируете вероятность того, что пользователь забыл вызвать функцию «init()». – chutsu

2

Это некрасиво, и я не рекомендую его, но если вы не можете бросить в конструкторе вы могли бы иметь немого конструктор и функцию инициализации:

class Foo 
{ 
private: 
    AnotherClass a; 
public: 
    Foo(){}; 
    bool initialize() 
    { 
     return a.Initialize(); 
    } 
    ... 
}; 

Foo obj; 
+0

Это то же самое, что я описал как Create()/Destroy(). – sevity

+0

Это может быть единственный работоспособный метод. Проблема в том, что конструкторы могут выполняться неявно. Когда они выполняются неявно, вы не можете проверить статус. –

0
class Foo 
{ 
private: 
    AnotherClass a; 
    bool m_bInitFail; //store the status of initialization failure 

public: 
    Foo(bool bInitFail = false) : m_bInitFail(bInitFail) 
    { 
     m_bInitFail = a.Initialize();   
    } 

    bool GetInitStatus() { return m_bInitFail ; } 
}; 

int main() 
{ 
    Foo fobj; 
    bool bStatus = fobj.GetInitStatus(); 
    return 0;   
} 
2

C++ без исключений по существу является совершенно другим языком для C++, в котором многие из идиом, которые дают C++ его уникальную выразительную силу, оказываются импотентами. Как вы заметили, конструкторы лишены своей полезности, и вся нетривиальная инициализация должна быть перенесена на псевдоконструктор второй ступени, который может возвращать индикацию ошибки. (Некоторые люди также выступают за подходящий псевдодеструктор из ошибочного чувства симметрии, но это совершенно бессмысленно). В качестве альтернативы конструктор может установить «построенный» флаг при успехе, и каждый класс может иметь «сконструированный» метод, который проверяет это и все его дочерние элементы.

Если ваша компания уполномочивает вас отключать исключения, для ее замены вам также понадобится общее (или, по крайней мере, общее) соглашение по всей компании. Вам нужно будет определить тип для всех (нетривиальных) функций для возврата и использовать их повсеместно - иначе вы получите недопустимый hodge-podge из булевых элементов и несовместимые перечисления, которые будут переданы и обработаны вручную на каждом уровне.

В этом случае Foo также понадобится метод Initialize, который вызывает a.Initialize и выдает справку, если это не удастся.

2

Я пробовал все альтернативы исключениям, которые я мог найти (переменная переменной члена, даже setjmp/longjmp), и все они сосали свой особый путь. Очень редко шаблон, который я полюбому проходит ссылку на объект ошибки вокруг, и проверка, если ошибка находится на рассмотрении в качестве самой первой операции в любой функции:

int function1(Error& e, char * arg) 
{ 
    if(e.failure()) 
     return -1; // a legal, but invalid value 

    // ... 
} 

int function2(Error& e, int arg) 
{ 
    if(e.failure()) 
     return -1; // a legal, but invalid value 

    // ... 
} 

int function3(Error& e, char * arg) 
{ 
    if(e.failure()) 
     return -1; 

    // if function1 fails: 
    // * function2 will ignore the invalid value returned 
    // * the error will cascade, making function2 "fail" as well 
    // * the error will cascade, making function3 "fail" as well 
    return function2(e, function1(e, arg)); 
} 

С некоторой работой, это относится конструкторам, а также:

class Base1 
{ 
protected: 
    Base1(Error& e) 
    { 
     if(e.failure()) 
      return; 

     // ... 
    } 

// ... 
}; 

class Base2 
{ 
protected: 
    Base2(Error& e) 
    { 
     if(e.failure()) 
      return; 

     // ... 
    } 

// ... 
}; 

class Derived: public Base1, public Base2 
{ 
public: 
    Derived(Error& e): Base1(e), Base2(e) 
    { 
     if(e.failure()) 
      return; 

     ... 
    } 
}; 

основной вопрос вы не получите автоматическое удаление в случае, если конструктор динамически выделяемый объект терпит неудачу. Я типично обернуть инвокации нового в функции следующим образом:

// yes, of course we need to wrap operator new too 
void * operator new(Error& e, size_t n) 
{ 
    if(e.failure()) 
     return NULL; 

    void * p = ::operator new(n, std::nothrow_t()); 

    if(p == NULL) 
     /* set e to "out of memory" error */; 

    return p; 
} 

template<class T> T * guard_new(Error& e, T * p) 
{ 
    if(e.failure()) 
    { 
     delete p; 
     return NULL; 
    } 

    return p; 
} 

, который будет использоваться как это:

Derived o = guard_new(e, new(e) Derived(e)); 

Преимущества этого метода включают в себя:

  • совместимость с C (если класс ошибки объявлен соответствующим образом)
  • потолочный сейф
  • ze ro накладные расходы в классах
  • Класс ошибки может быть непрозрачным на 100%; с использованием макросов для доступа, объявления и передачи его, он может включать в себя все виды информации, включая, но не ограничиваясь, исходный файл и строку, имя функции, обратную трассировку стека и т. д.
  • применяется к довольно сложным выражениям, что делает его почти как исключения во многих случаях
+0

Кстати, я не беру на себя ответственность за эту технику. Я думаю, что IBM придумала это, потому что единственное место, которое я когда-либо видел, это в их библиотеке ICU – KJKHyperion

3

безопасно избегать наличия кода, который вызывает сбои внутри конструктора или дескриптора.Попросите еще одного члена: bool Initialize(), bool Uninitialize() иметь такие коды.

Смежные вопросы