2009-06-19 20 views
6

To jest klasa C++, którą zrobiłem z n liczbą wskaźników.robienie licznych wskaźników NULL w tym samym czasie

class SomeClass 
{ 
private: 
     int* ptr1; 
     int* ptr2; 
     ... 
     int* ptrn; 

private: 
     // constructors, destructors, and methods 
}; 

Podczas fazy inicjalizacji, chcę, aby wszystkie te wskaźniki wskaż polecenie NULL (lub uczynić wskaźniki wskaż polecenie NULL domyślnie, gdy są one zadeklarowanej) zamiast tego:

void SomeClass::MakePtrNull() 
{ 
     ptr1 = NULL; 
     ptr2 = NULL; 
     ... 
     ptrn = NULL; 
} 

Czy istnieje jakąkolwiek łatwą metodę osiągnięcia tego celu? Zastanawiam się, czy istnieją sposoby uniknięcia wpisywania n linii ptr = NULL; w mojej funkcji. Z góry dziękuję.

DODANO oparty na odpowiedziach, że otrzymałem dotąd: Niestety, te wskaźniki muszą być oddzielone, jak są one wykorzystywane do różnych celów. Zrobiłem nazwy wskaźników jako takie tylko po to, aby ustalić, co próbuję zrobić, ale każda z nich ma zupełnie inny cel. Sądzę, że musiałbym sprawić, by wskazywały na NULL, jak już zrobiłem. Dziękuję za odpowiedzi.

+0

Należy zauważyć, że jeśli klasa nie ma konstruktora deklarującego użytkownika, SomeClass() będzie mieć wszystkie wskaźniki zerowe. Oznacza to, że możesz po prostu zrobić * this = SomeClass(); i uczyniłoby wszystkie wskaźniki wskaźnikami zerowymi. –

+0

@litb, w moich testach tylko statyczne instancje SomeClass mają wyzerowane wszystkie wskaźniki; automatyczne i sterty instancji pobierają wszystko, co leży w stosie lub sterty. –

+0

przy założeniu, że nie ma konstruktora deklarowanego przez użytkownika, wówczas SomeClass() jest inicjowany w przeciwieństwie do "a" w {SomeClass a; }. Wartość SomeClass() - inicjuje, rekurencyjnie, wszystkie jego elementy. Spowoduje to, że dowolne elementy wskaźnika będą miały wartości zerowe. Jeśli zdefiniujesz zmienną automatyczną i nie ma ona deklarowanego konstruktora, nie zostanie ona zainicjalizowana. Musiałbyś zrobić na przykład {SomeClass a = SomeClass(); } –

Odpowiedz

6

Dlaczego nie używasz tablicy lub wektora, zamiast tworzyć n pojedynczo wskazanych wskaźników? Następnie możesz wykonać zerowanie w krótkiej pętli for.

5

Można to zrobić:

void SomeClass::MakePtrNull() 
{ 
     ptr1 = ptr2 = ptr3 = ... = ptrn = NULL; 
} 
3

pierwsze, technikę, która nie praca:

Wywołanie memset aby ustawić cały obiekt do zera nie zrobi. Po pierwsze, spowoduje to wiele problemów, jeśli twoja funkcja ma jedną lub więcej funkcji wirtualnych, a po drugie, wskaźnik zerowy nie może być reprezentowany przez wzorzec bitowy wszystkich zer.

To, co prawdopodobnie zrobiłbym w twoim przypadku, to przechowywanie wskaźników w tablicy lub wektorze. Następnie można użyć funkcji std::fill, aby ustawić wszystkie na NULL. (Lub możesz użyć pętli, jeśli wolisz)

Oczywiście, jeśli chcesz zrobić to wystarczająco często, może warto napisać klasę wrapper, która zachowuje się jak wskaźnik, ale która ustawia ją na NULL w swoim domyślnym konstruktor.

Lub możesz użyć boost :: optional, który działa w zasadzie tak jak to. (Choć nie jest to specyficzne dla wskaźników)

+1

Mówisz, że zerowy wskaźnik nie ma gwarancji przedstawienia przez wzorzec bitowy wszystkich zer, co oznacza, że ​​jeśli kontrola taka jak ta: jeśli (ptr1) nie jest bezpiecznym sposobem określenia, czy wskaźnik nie ma wartości null ? –

+0

Tak, proszę, wyjaśnij, w jakim scenariuszu 0x0 nie jest prawidłowym adresem "brak pamięci przydzielonej tutaj". Ponadto, jeśli struktura/klasa (jak również jej klasy macierzyste) nie mają metod wirtualnych, wówczas jej sizeof będzie dokładnie sumą sizeofs jej członków. –

+1

@ średnie. To jest bezpieczne. NULL ptr musi oceniać fałsz, ale implementacja może reprezentować go w pamięci, jakkolwiek mu się podoba. W praktyce prawie wszyscy używają 0, ponieważ NULL ptr również musi rzutować na liczbę całkowitą jako 0 i zwykle jest najłatwiejsze i najbardziej wydajne, aby nie zmieniać wzorca bitowego. @Shane: struct musi być POD, ponieważ to, co mówisz, jest gwarantowane przez standard. Istnieją inne rzeczy niż wirtualne metody, które mogą powodować, że struktura nie jest POD, a każda z klas bazowych jest jedną z nich. –

2

Dlaczego nie używać domyślnego konstruktora:

SomeClass::SomeClass() : ptr1(NULL), ptr2(NULL), ... 
{ 

} 

Można również zrobić:

ptr1 = ptr2 = ptr3 = NULL; 
0

Użyj wektor do tego (bo przez dźwięki nie będziesz już musiał edytować tej listy w przyszłości - i potrzebujesz dostępu losowego). Można to zrobić tak:

class PointerList 
{ 
private: 
    typedef std::vector<int*> IntPtrVector; 
    IntPtrVector m_ptrVec; 

public: 
    PointerList() 
    { 
     m_ptrVec.reserve(5); 
     for (int i = 0; i < 5; i++) 
      m_ptrVec.push_back(NULL); 
    } 

    int* getPointer(int index) 
    { 
     return m_ptrVec[index]; 
    } 

    void setPointer(int index, int* ptr) 
    { 
     assert(index <= 4); 
     m_ptrVec[index] = ptr; 
    } 
}; 

EDIT

Choć szczerze mówiąc to cuchnie ghettoness. Czy na pewno Twój problem wymaga tego rozwiązania? Jeśli rozwiążesz więcej na temat konkretnego problemu, na pewno w innym pytaniu, jestem pewien, że możesz uzyskać lepszą odpowiedź na pytanie, jak osiągnąć to, co chcesz, bardziej elegancko - zamiast tworzyć drugi problem.

+0

Cały problem polega na pytaniu, czy mogę leniwego wyjścia, ale działa. Dziękuję za poświęcony czas na sugestie. – stanigator

11

Zamiast int *, utworzyć klasy smart-pointer-podobne, które działa dokładnie jak int *, ale default-konstrukty z NULL:

template <typename T> 
class MyPointer { 
    T *p; 

public: 
    MyPointer() : p(NULL) { } 
    MyPointer(T *o) : p(o) { } 
    operator T*() const { return p; } 
    // ...and the rest of the traditional smart-pointer operators 
}; 

Następnie wykorzystać go w swojej klasie:

class SomeClass 
{ 
private: 
     MyPointer<int> ptr1; 
     MyPointer<int> ptr2; 
     ... 
     MyPointer<int> ptrn; 

private: 
     // constructors, destructors, and methods 
}; 

Każda zmienna typu MyPointer<int> zostanie automatycznie zainicjowana poprawnie w konstruktorach SomeClass, bez konieczności dodatkowego pisania. Jeśli nie zapomnisz lub niepoprawnie zaimplementujesz jedną z metod MyPointer, będzie ona działać dokładnie tak, jak normalny wskaźnik i ma dokładnie taki sam rozmiar i wydajność.

+2

+1 za użycie systemu typu do dokładnego wyrażenia zamiaru programowania. –

+0

Inną korzyścią jest to, że opiekunowie kodu i nie muszą inicjować wskaźników w każdym konstruktorze ani pamiętać o aktualizacji list inicjalizujących w przypadku zmiany liczby lub nazwy członków MyPointer. –

+0

Również - 0 runtime napowietrznych – Eclipse

1

Można umieścić wskaźniki w struct, a następnie memset() struct, gdy jest to konieczne. Wskaźniki są nadal rozdzielone, ale masz możliwość kierowania ich jako jednej jednostki bez wpływu na resztę klasy. Na przykład:

struct MyPointers 
{ 
    int* ptr1; 
    int* ptr2; 
    ... 
    int* ptrn; 
}; 

class SomeClass 
{ 
private: 
    MyPointers ptrs; 
    ... 
}; 

void SomeClass::MakePtrNull() 
{ 
    memset(&ptrs, 0, sizeof(ptrs)); 
} 
+1

memset nie działa w ten sposób. zobacz odpowiedź Jalfa. –

3

polecam Ci wykonać następujące czynności, jeśli masz do utrzymania wskaźników oddzielnie (chyba najbardziej naglące potrzeby byłoby gdyby wskaźniki mogą mieć różne rodzaje)

class SomeClass { 
    struct Pointers { 
     int* ptr1; 
     int* ptr2; 
     float* ptrn; 
    } ptrs; 

public: 
    void MakePtrNull(); 
}; 

void SomeClass::MakePtrNull() 
{ 
    // clears all pointers at once 
    ptrs = Pointers(); 
} 

ten działa, ponieważ inicjowanie wartości dla klas, które nie mają zadeklarowanego konstruktora, spowoduje zainicjowanie wartości wszystkich jego członków. Inicjalizacja wartości wskaźnika spowoduje utworzenie wskaźnika pustego.

+0

To świetna technika. +1 –

Powiązane problemy