2011-09-26 14 views
33

w C++, chciałbym zdefiniować obiekt jako członek klasy jak ten:Określenie przedmiotu bez wywołanie konstruktora w C++

Object myObject; 

Jednak robi to spróbuje nazwać to bez parametrów konstruktora, który nie robi istnieje. Jednak potrzebuję wywołanie konstruktora po tym, jak klasa zawierająca dokonała inicjalizacji. Coś takiego.

class Program 
{ 
public: 
    Object myObject; //Should not try to call the constructor or do any initializing 
    Program() 
    { 
     ... 

     //Now call the constructor 
     myObject = Object(...); 
    } 

} 
+4

Dlaczego nie używać dynamicznej inicjalizacji? auto_ptr/shared_ptr? – qehgt

+1

Globalny zasięg lub członek klasy? Twój kod nie pasuje do Twojego pytania. –

+0

Co jest nie tak z wywołaniem domyślnego konstruktora, a po inicjalizacji ustawiasz go na obiekt, na którym ci zależy, dokładnie tak, jak robi to twój kod? Lub po prostu uczyń go wskaźnikiem: 'Object * myObj;' – Chad

Odpowiedz

20

Store wskaźnikiem do Object zamiast rzeczywistej Object

tak:

class Program 
{ 
public: 
    Object* myObject; // Will not try to call the constructor or do any initializing 
    Program() 
    { 
     //Do initialization 
     myObject = new Object(...); // Initialised now 
    } 

} 

Nie zapomnij delete to w destructor. Współczesne C++ pomaga ci tam, ponieważ możesz użyć raczej wspólnego parametru zamiast zmiennego wskaźnika pamięci.

+4

Jeśli tworzysz destruktor, powinieneś przestrzegać reguły trzech. – Sardathrion

+0

dlaczego nie 'std :: unique_ptr' zamiast udostępniania? –

+0

@RomanKruglov • a 'std :: unique_ptr' jest w porządku, o ile również' = usuń 'konstruktora kopiowania i operatora przypisania lub implementuj je tak, aby postępować właściwie. – Eljay

1

Możesz użyć wskaźnika (lub inteligentnego wskaźnika), aby to zrobić. Jeśli nie używasz inteligentnego wskaźnika, upewnij się, że pamięć jest wolna od kodu, gdy obiekt jest usuwany. Jeśli używasz inteligentnego wskaźnika, nie martw się o to.

class Program 
{ 
public: 
    Object * myObject; 
    Program(): 
     myObject(new Object()) 
    { 
    } 
    ~Program() 
    { 
     delete myObject; 
    } 
    // WARNING: Create copy constructor and = operator to obey rule of three. 
} 
16

Inni napisali rozwiązania za pomocą surowych wskaźników, ale inteligentny wskaźnik byłby lepszy pomysł:

class MyClass { 
    std::unique_ptr<Object> pObj; 
    // use boost::scoped_ptr for older compilers; std::unique_ptr is a C++0x feature 
public: 
    MyClass() { 
    // ... 
    pObj.reset(new Object(...)); 
    pObj->foo(); 
    } 
    // Don't need a destructor 
}; 

Eliminuje to potrzebę dodania destruktor, a pośrednio zabrania kopiowania (chyba piszesz swoje . własna operator= i MyClass(const MyClass &)

Jeśli chcesz uniknąć osobnego alokację sterty, można to zrobić z doładowania na aligned_storage i umieszczenie nowego Nietestowane.

template<typename T> 
class DelayedAlloc : boost::noncopyable { 
    boost::aligned_storage<sizeof(T)> storage; 
    bool valid; 
public: 
    T &get() { assert(valid); return *(T *)storage.address(); } 
    const T &get() const { assert(valid); return *(const T *)storage.address(); } 

    DelayedAlloc() { valid = false; } 

    // Note: Variadic templates require C++0x support 
    template<typename Args...> 
    void construct(Args&&... args) 
    { 
    assert(!valid); 
    new(storage.address()) T(std::forward<Args>(args)...); 
    valid = true; 
    } 

    void destruct() { 
    assert(valid); 
    valid = false; 
    get().~T(); 
    } 

    ~DelayedAlloc() { if (valid) destruct(); } 
}; 

class MyClass { 
    DelayedAlloc<Object> obj; 
public: 
    MyClass() { 
    // ... 
    obj.construct(...); 
    obj.get().foo(); 
    } 
} 

Lub, jeśli Object jest copyable (lub ruchome), można użyć boost::optional:

class MyClass { 
    boost::optional<Object> obj; 
public: 
    MyClass() { 
    // ... 
    obj = Object(...); 
    obj->foo(); 
    } 
}; 
+0

Korzystając z pierwszej sugestii, otrzymuję 'Tekst"> "jest nieoczekiwany. Być może ten token był pomyślany jako szablon terminatora listy argumentów, ale nazwa nie jest znana jako szablon " – Anonymous

5

Jeśli masz dostęp do pobudzenia, jest przydatny przedmiot, który jest dostarczany nazywa boost::optional<> - pozwala to uniknąć potrzeba alokacji dynamicznej, np

class foo 
{ 
    foo() // default std::string ctor is not called.. 
    { 
    bar = boost::in_place<std::string>("foo"); // using in place construction (avoid temporary) 
    } 
private: 
    boost::optional<std::string> bar; 
}; 
2

może także być w stanie przepisać kod do korzystania z listy konstruktor initializer, jeśli można ruszyć drugą inicjację do konstruktorów:

class MyClass 
    { 
    MyObject myObject; // MyObject doesn't have a default constructor 
    public: 
    MyClass() 
     : /* Make sure that any other initialization needed goes before myObject in other initializers*/ 
     , myObject(/*non-default parameters go here*/) 
     { 
     ... 
     } 
    }; 

Trzeba mieć świadomość, że w wyniku takiego wzorzec doprowadzi cię do ścieżki, w której wykonujesz dużo pracy w konstruktorze, co z kolei prowadzi do konieczności uchwycenia obsługi wyjątków i bezpieczeństwa (ponieważ kanonicznym sposobem zwrócenia błędu z konstruktora jest zgłoszenie wyjątku).

Powiązane problemy