Dlaczego wskaźniki nie są zalecane podczas kodowania w C ++?


45

Czytałem skądś, że podczas używania C ++ zaleca się nie używać wskaźników. Dlaczego wskaźniki są tak złym pomysłem, gdy używasz C ++. Dla programistów C przyzwyczajonych do używania wskaźników, jaka jest lepsza alternatywa i podejście w C ++?


40
proszę link do „gdzieś”. Kontekst może być bardzo istotny.

1
Mam nadzieję, że to pytanie jest dla Ciebie przydatne.
Garet Claborn,

Większość tych odpowiedzi odnosi się do unikania wycieków pamięci jako głównego powodu. Nie pamiętam, kiedy ostatni raz jedna z naszych aplikacji miała problem z wyciekiem pamięci pomimo używania wskaźników. Jeśli masz problemy z wyciekiem pamięci, oznacza to, że nie używasz odpowiednich narzędzi lub nie wiesz, co robisz. Większość środowisk programistycznych ma sposób na automatyczne sprawdzanie wbudowanych wycieków. Myślę, że problemy z wyciekiem pamięci są znacznie trudniejsze do wyśledzenia w śmieciowych językach, ponieważ ich występowanie jest znacznie bardziej subtelne i często potrzebujesz zewnętrznego narzędzia do śledzenia winowajcy .
Dunk

1
Dodając do komentarza @Dunk, czasami wbudowane moduły do ​​śmieci w językach wyższego poziomu po prostu nie działają poprawnie. Na przykład moduł śmieciowy ActionScript 3 nie. Jest w tym teraz błąd, który dotyczy NetConnectioninstancji rozłączających się z serwerem ( stackoverflow.com/questions/14780456/... ), a także problem z występowaniem wielu obiektów w programie, których konkretnie odmawia gromadzenia ...
Panzercrisis,

... ( adobe.com/devnet/actionscript/learning/as3-fundamentals/… - wyszukaj GCRoots are never garbage collected.i akapit zacznij od The MMgc is considered a conservative collector for mark/sweep.). Z technicznego punktu widzenia jest to problem w programie Adobe Virtual Machine 2, a nie w samym AS3, ale gdy masz takie problemy w językach wyższego poziomu, które mają wbudowane funkcje czyszczenia pamięci, często nie masz żadnego prawdziwego sposobu na debugowanie w języku te problemy całkowicie poza programem. ...
Panzercrisis,

Odpowiedzi:


58

Myślę, że oznaczają, że powinieneś używać inteligentnych wskaźników zamiast zwykłych wskaźników.

W informatyce inteligentny wskaźnik to abstrakcyjny typ danych, który symuluje wskaźnik, zapewniając jednocześnie dodatkowe funkcje, takie jak automatyczne wyrzucanie elementów bezużytecznych lub sprawdzanie granic. Te dodatkowe funkcje mają na celu ograniczenie błędów spowodowanych niewłaściwym użyciem wskaźników przy jednoczesnym zachowaniu wydajności. Inteligentne wskaźniki zwykle śledzą obiekty, na które wskazują, w celu zarządzania pamięcią.

Niewłaściwe użycie wskaźników jest głównym źródłem błędów: ciągłe przydzielanie, zwalnianie i porównywanie, które musi zostać wykonane przez program napisany przy użyciu wskaźników, wprowadza ryzyko wycieku pamięci. Inteligentne wskaźniki próbują zapobiegać wyciekom pamięci, czyniąc dezalokację zasobów automatyczną: gdy wskaźnik (lub ostatni z serii wskaźników) do obiektu zostanie zniszczony, na przykład dlatego, że wykracza poza zakres, wskazany obiekt również zostaje zniszczony.

W C ++ nacisk kładziony byłby na wyrzucanie elementów bezużytecznych i zapobieganie wyciekom pamięci (żeby wymienić tylko dwa). Wskaźniki są podstawową częścią języka, więc nieużywanie ich jest prawie niemożliwe, z wyjątkiem najbardziej trywialnych programów.


22
zazwyczaj nie jest to wyłącznie wywóz śmieci w klasycznym znaczeniu, więcej liczeń referencyjnych. Przynajmniej w inteligentnym ptr, do którego jestem przyzwyczajony ([boost | std] :: shared_ptr)
Doug T.

3
Ta odpowiedź jest bardzo ograniczona do inteligentnego wskaźnika, który jest tylko małym aspektem problemu. Ponadto inteligentny wskaźnik nie jest wyrzucaniem elementów bezużytecznych.
deadalnix

3
Również ogólnie RAII.
Klaim

3
Nie, nie o to chodzi. To tylko jeden aspekt, a nie najważniejszy.
Konrad Rudolph

Myślę, że inteligentne wskaźniki są najważniejszym aspektem, ale zgadzam się, że istnieje wiele innych.
DeadMG

97

Ponieważ jestem tym, który opublikował polemikę „nie używaj wskazówek, kurwa” , czuję, że powinienem tutaj skomentować.

Po pierwsze, jako polemika, reprezentuje oczywiście skrajny punkt widzenia. Tam zdecydowanie uzasadnione zastosowania (RAW) wskaźniki. Ale ja (i wielu profesjonalnych programistów C ++) twierdzę, że przypadki te są niezwykle rzadkie. Ale tak naprawdę mamy na myśli:

Pierwszy:

Surowe wskaźniki nie mogą w żadnym wypadku posiadać własnej pamięci.

Tutaj „własna pamięć” zasadniczo oznacza, że ​​w pewnym momencie deletewywoływany jest ten wskaźnik (ale jest on bardziej ogólny). To stwierdzenie można bezpiecznie uznać za absolutne. Jedyny wyjątek to podczas wdrażania własnego inteligentnego wskaźnika (lub innych strategii zarządzania pamięci). I nawet tam zwykle powinieneś nadal używać inteligentnego wskaźnika na niskim poziomie.

Uzasadnienie tego jest dość proste: surowe wskaźniki, których własna pamięć wprowadza źródło błędu. Błędy te są liczne w istniejącym oprogramowaniu: wycieki pamięci i podwójne usuwanie - oba są bezpośrednią konsekwencją niejasnego posiadania zasobów (ale idą w przeciwnym kierunku).

Problem ten można całkowicie wyeliminować, praktycznie bez żadnych kosztów, po prostu używając inteligentnych wskaźników zamiast wskaźników surowych (zastrzeżenie: to oczywiście wymaga myślenia, oczywiście; wspólne wskaźniki mogą prowadzić do cykli, a tym samym do wycieków pamięci - ale jest to łatwe do uniknięcia).

Druga:

Większość zastosowań wskaźników w C ++ jest niepotrzebna.

W przeciwieństwie do innych języków, C ++ ma bardzo silne wsparcie dla semantyki wartości i po prostu nie potrzebuje pośrednictwa wskaźników. Nie zostało to natychmiast zrealizowane - historycznie C ++ został wymyślony w celu ułatwienia łatwej orientacji obiektów w C i polegał w dużej mierze na tworzeniu grafów obiektowych połączonych wskaźnikami. Ale we współczesnym C ++ ten paradygmat rzadko jest najlepszym wyborem, a współczesne idiomy C ++ często nie potrzebują wskaźników . Działają na wartościach, a nie na wskaźnikach.

Niestety, ta wiadomość wciąż nie została przyjęta w dużej części społeczności użytkowników C ++. W rezultacie większość napisanego kodu C ++ jest wciąż wypełniona zbędnymi wskaźnikami, które sprawiają, że kod jest złożony, powolny i wadliwy / zawodny.

Dla kogoś, kto zna nowoczesnych C ++, to jasne, że bardzo rzadko trzeba żadnych wskazówek (zarówno inteligentne lub nieprzetworzone, z wyjątkiem gdy wykorzystując je jako iteratory). Powstały kod jest krótszy, mniej złożony, bardziej czytelny, często bardziej wydajny i bardziej niezawodny.


5
Westchnienie ... to naprawdę powinna być odpowiedź z ponad 30 głosami pozytywnymi ... szczególnie w odniesieniu do drugiego punktu. Wskaźniki są po prostu rzadko potrzebne nawet we współczesnym C ++.
Charles Salvia,

1
co z np. Obiekt GUI jest właścicielem wielu obiektów doc. Ma to zarówno jako wskaźniki, aby klasa mogła być zadeklarowana do przodu (unika się ponownej kompilacji) i aby obiekt mógł zostać zainicjowany podczas tworzenia (z nowym), zamiast być tworzony na stosie w jakimś pustym stanie, a następnie wprowadzany później? Wydaje się, że jest to całkowicie poprawne użycie wskaźników - czy wszystkie zamknięte obiekty nie powinny znajdować się na stosie?
Martin Beckett,

4
Obiekty @Martin GUI to jeden przypadek, w którym wykresy obiektów wskaźnikowych są rzeczywiście najlepszym rozwiązaniem. Ale edykt przeciwko surowym wskaźnikom będącym właścicielem pamięci nadal obowiązuje. Użyj wskaźników wspólnych w całym tekście lub opracuj odpowiedni model własności i wykaż tylko słabe (= nieposiadające) relacje między kontrolami za pomocą wskaźników surowych.
Konrad Rudolph

1
@ VF1 std::unique_ptr. A czemu nie ptr_vec? Ale zazwyczaj wektor wartości z nadal będzie zamieniał się szybciej (szczególnie w przypadku semantyki ruchu).
Konrad Rudolph

1
@gaazkam Nikt nie zmusił cię do używania tylko standardowych pojemników. Na przykład Boost ma implementację mapy z obsługą niekompletnych typów. Innym rozwiązaniem jest użycie kasowania typu; boost::variantz recursive_wrapperjest prawdopodobnie moim ulubionym rozwiązaniem do reprezentowania DAG.
Konrad Rudolph

15

Po prostu dlatego, że masz do dyspozycji abstrakcje, które ukrywają bardziej temperamentne aspekty używania wskaźników, takie jak dostęp do surowej pamięci i sprzątanie po twoich przydziałach. Dzięki inteligentnym wskaźnikom, klasom kontenerów i wzorcom projektowym, takim jak RAII, potrzeba korzystania z surowych wskaźników jest zmniejszona. To powiedziawszy, jak każda abstrakcja, powinieneś zrozumieć, jak one faktycznie działają, zanim przejdziesz dalej.


11

Stosunkowo po prostu mentalność C brzmi „Masz problem? Użyj wskaźnika”. Możesz to zobaczyć w ciągach C, wskaźnikach funkcji, wskaźnikach jako iteratorach, wskaźnikach do wskaźników, wskaźnikach pustych - nawet na początku C ++ ze wskaźnikami składowymi.

Ale w C ++ możesz użyć wartości do wielu lub wszystkich tych zadań. Potrzebujesz abstrakcji funkcji? std::function. Jest to wartość, która jest funkcją. std::string? To wartość, to ciąg. Podobne podejścia można zobaczyć w całym C ++. Dzięki temu analiza kodu jest znacznie łatwiejsza zarówno dla ludzi, jak i kompilatorów.


W C ++: Masz problem? Użyj wskaźnika. Teraz masz 2 problemy ...
Daniel Zazula,

10

Jednym z powodów jest zbyt szerokie zastosowanie wskaźników. Można ich używać do iteracji po kontenerach, do unikania kopiowania dużych obiektów podczas przechodzenia do funkcji, niebanalnego zarządzania czasem życia, dostępu do losowych miejsc w pamięci itp. A gdy użyjesz ich do jednego celu, inne ich funkcje stają się dostępne natychmiast niezależnie od zamiaru.

Wybór narzędzia do konkretnego celu sprawia, że ​​kod jest prostszy i bardziej czytelny - iteratory iteracji, inteligentne wskaźniki do zarządzania czasem życia itp.


3

Poza wymienionymi już przyczynami jest oczywisty: lepsze optymalizacje. Analiza aliasingu jest zdecydowanie zbyt skomplikowana pod względem arytmetyki wskaźnika, podczas gdy referencje wskazują optymalizator, więc o wiele głębsza analiza aliasingu jest możliwa, jeśli używane są tylko referencje.


2

Oprócz ryzyka wycieków pamięci wskazanego przez wskaźnik @jmquigley i arytmetykę wskaźnika można uznać za problematyczne, ponieważ wskaźniki mogą wskazywać wszędzie w pamięci, powodując „trudne do znalezienia błędy” i „luki w zabezpieczeniach”.

Właśnie dlatego zostali prawie porzuceni na C # i Javie.


Spodziewaj się, że nie zostały one „porzucone” w języku C #. Ta odpowiedź jest niska, ma okropną pisownię i okropne, niedokładne stwierdzenie.
Ramhound,

1
Byli prawie opuszczeni. Przepraszam, że nie jestem native speakerem.
k3b

1
Hej, możemy pomóc w pisowni. Czy chodziło Ci o oczekiwanie czy wyjątek?
DeveloperDon

1
Prawie porzucony w C #, nadal możesz włączyć wskaźnik, określając unsafesłowo kluczowe
linquize

-1

C ++ obsługuje większość C , funkcji oraz Obiekty i Klasy. C miał już wskaźniki i inne rzeczy.

Wskaźniki są bardzo przydatną techniką, którą można łączyć z Object Orientation, a C ++ je obsługuje. Ale ta technika jest trudna do nauczenia i trudna do zrozumienia, a jej bardzo łatwo jest powodować niepożądane błędy.

Wiele nowych języków programowania udaje, że nie używa wskaźników ze obiektami, takich jak Java, .NET, Delphi, Vala, PHP, Scala. Ale wskaźniki są nadal używane „za kulisami”. Te techniki „ukrytego wskaźnika” są nazywane „referencjami”.

W każdym razie uważam wskaźnik (i) za wzorzec programowania, jako prawidłowy sposób rozwiązywania niektórych problemów, podobnie jak programowanie obiektowe .

Inni programiści mogą mieć inne zdanie. Ale proponuję, aby uczniowie i programiści dowiedzieli się, jak:

(1) Używaj wskaźników bez obiektów

(2) obiekty bez wskaźników

(3) wyraźne wskaźniki do obiektów

(4) „ukryte” wskaźniki do obiektów ( odniesienie AKA ) ;-)

W tej kolejności.

Nawet jeśli jest trudny do nauczenia i trudny do nauczenia. DoC++ tych celów można użyć Object Pascal (Delphi, FreePascal, inne) i (nie Java ani C #).

Później nowi programiści mogą przejść do języków programowania „ukryte wskaźniki do obiektów”, takich jak: Java, C #, PHP zorientowany obiektowo i inne.


19
C ++ to o wiele więcej niż „C with Classes”, od którego zaczął.
David Thornley,

Dlaczego umieszczasz C ++ i C w cudzysłowie? I „ukryte”, „referencje” i wszystko inne? Czy jesteś „sprzedawcą” i nie uczestniczysz w „programowaniu”?
fresnel

Powinienem je pogrubić. Cudzysłowy można wykorzystać do podkreślenia, ale także do odwrotności
umlcat,

-6

Mówiąc o VC6, kiedy rzutujesz wskaźnik klasy (tworzony przez instancję) na zmienną (np. DWORD), nawet jeśli ten wskaźnik jest lokalny, możesz uzyskać dostęp do klasy przez wszystkie funkcje, które używają tej samej sterty. Instancja klasy jest zdefiniowana jako lokalna, ale w rzeczywistości tak nie jest. O ile mi wiadomo, każdy adres zmiennej, struktury lub klasy sterty jest unikalny przez cały okres istnienia klasy hostującej.

Przykład:

class MyClass1 {
    public:
        void A (void);
        void B (void);
        void C (void);
    private:
        DWORD dwclass;
};

class MyClass2 {
    public:
        int C (int i);
};

void MyClass1::A (void) {
    MyClass2 *myclass= new MyClass2;
    dwclass=(DWORD)myclass;
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    int i = myclass->C(0); // or int i=((MyClass2 *)dwclass)->C(0);
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    delete myclass;
}

EDYCJA To bardzo mała część oryginalnego kodu. Klasa CSRecodset to tylko klasa rzutowania CXdbRecordset, w której znajduje się cały prawdziwy kod. W ten sposób mogę pozwolić użytkownikowi skorzystać z tego, co napisałem, bez utraty moich praw. Nie udaję, że demonstruję, że mój silnik bazy danych jest profesjonalny, ale naprawdę działa.

//-------------------------------------
class CSRecordSet : public CSObject
//-------------------------------------
{
public:
    CSRecordSet();
    virtual ~CSRecordSet();
    // Constructor
    bool Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef);
    //Open, find, close
    int OpenRst(bool bReadBlanks=0,bool bCheckLastSql=0,bool bForceLoad=0,bool bMessage=1);     // for a given SQL
    int FindRecord(bool bNext);         // for a given SQL
    // TABLE must be ordered by the same fields that will be seek
    bool SeekRecord(int nFieldIndex, char *key, int length=0);  // CRT bsearch
    bool SeekRecord(int nFieldIndex, long key);     
    bool SeekRecord(int nFieldIndex, double key, int decimals);     
    bool SeekRecord(XSEK *SEK);     
    bool DeleteRecord(void);
    bool Close(void);
    // Record Position:
    bool IsEOF(void);           // pointer out of bound
    bool IsLAST(void);          // TRUE if last record
    bool IsBOF(void);           // pointer out of bound
    bool IsOpen(void);
    bool Move(long lRows);      // returns FALSE if out of bound
    void MoveNextNotEof(void);  // eof is tested
    void MoveNext(void);        // eof is not tested
    void MovePrev(void);        // bof is tested
    void MoveLast(void);
    void MoveFirst(void);
    void SetAbsolutePosition(long lRows);
    long GetAbsolutePosition(void);
    void GoToLast(void); // Restore position after a Filter
    // Table info
    long GetRecordCount(void);
    int GetRstTableNumber(void);
    int GetRecordLength(void); //includes stamp (sizeof char)
    int GetTableType(void);
    // Field info
    int GetFieldCount(void);
    void GetFieldName(int nFieldIndex, char *pbuffer);
    int GetFieldIndex(const char *sFieldName);
    int GetFieldSize(int nFieldIndex);
    int GetFieldDGSize(int nFieldIndex); // String size (i.e. dg_Boolean)
    long GetRecordID(void);
    int GetStandardFieldCount(void);
    bool IsMemoFileTable(void);
    bool IsNumberField(int nFieldIndex);
    int GetFieldType(int nFieldIndex);
    // Read Field value
    bool GetFieldValue(int nFieldIndex, XdbVar& var);
    bool GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer);
    char *GetMemoField(int nMemoFieldIndex, char *pbuffer, int buf_size);
    bool GetBinaryField(unsigned char *buffer,long *buf_size);
    // Write Field value
    void Edit(void); // required
    bool SetFieldValue(int nFieldIndex, XdbVar& var);
    bool SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer);
    bool Update(void); // required
    // pointer to the same lpSql
    LPXSQL GetSQL(void);
};

//---------------------------------------------------
CSRecordSet::CSRecordSet(){
//---------------------------------------------------
    pClass |= (CS_bAttach);
}
CSRecordSet::~CSRecordSet(){
    if(pObject) delete (CXdbRecordset*)pObject;
}
bool CSRecordSet::Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef){
    CXdbQueryDef *qr=(CXdbQueryDef*)pQueryDef->GetObject();
    CXdbTables *db=(CXdbTables*)pDataBase->GetObject();
    CXdbRecordset *rst = new CXdbRecordset(db,qr);
    if(rst==NULL) return 0;
    pObject = (unsigned long) rst;
    return 1;
}
bool CSRecordSet::Close(void){
    return ((CXdbRecordset*)pObject)->Close();
}
int CSRecordSet::OpenRst(bool bReadBlanks,bool bCheckLastSql,bool bForceLoad, bool bMessage){
    unsigned long dw=0L;
    if(bReadBlanks) dw|=SQL_bReadBlanks;
    if(bCheckLastSql) dw|=SQL_bCheckLastSql;
    if(bMessage) dw|=SQL_bRstMessage;
    if(bForceLoad) dw|=SQL_bForceLoad;

    return ((CXdbRecordset*)pObject)->OpenEx(dw);
}
int CSRecordSet::FindRecord(bool bNext){
    return ((CXdbRecordset*)pObject)->FindRecordEx(bNext);
}
bool CSRecordSet::DeleteRecord(void){
    return ((CXdbRecordset*)pObject)->DeleteEx();
}
bool CSRecordSet::IsEOF(void){
    return ((CXdbRecordset*)pObject)->IsEOF();
}
bool CSRecordSet::IsLAST(void){
    return ((CXdbRecordset*)pObject)->IsLAST();
}
bool CSRecordSet::IsBOF(void){
    return ((CXdbRecordset*)pObject)->IsBOF();
}
bool CSRecordSet::IsOpen(void){
    return ((CXdbRecordset*)pObject)->IsOpen();
}
bool CSRecordSet::Move(long lRows){
    return ((CXdbRecordset*)pObject)->MoveEx(lRows);
}
void CSRecordSet::MoveNextNotEof(void){
    ((CXdbRecordset*)pObject)->MoveNextNotEof();
}
void CSRecordSet::MoveNext(void){
    ((CXdbRecordset*)pObject)->MoveNext();
}
void CSRecordSet::MovePrev(void){
    ((CXdbRecordset*)pObject)->MovePrev();
}
void CSRecordSet::MoveLast(void){
    ((CXdbRecordset*)pObject)->MoveLast();
}
void CSRecordSet::MoveFirst(void){
    ((CXdbRecordset*)pObject)->MoveFirst();
}
void CSRecordSet::SetAbsolutePosition(long lRows){
    ((CXdbRecordset*)pObject)->SetAbsolutePosition(lRows);
}
long CSRecordSet::GetAbsolutePosition(void){
    return ((CXdbRecordset*)pObject)->m_AbsolutePosition;
}
long CSRecordSet::GetRecordCount(void){
    return ((CXdbRecordset*)pObject)->GetRecordCount();
}
int CSRecordSet::GetFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetFieldCount();
}
int CSRecordSet::GetRstTableNumber(void){
    return ((CXdbRecordset*)pObject)->GetRstTableNumber();
}
void CSRecordSet::GetFieldName(int nFieldIndex, char *pbuffer){
    ((CXdbRecordset*)pObject)->GetFieldName(nFieldIndex,pbuffer);
}
int CSRecordSet::GetFieldIndex(const char *sFieldName){
    return ((CXdbRecordset*)pObject)->GetFieldIndex(sFieldName);
}
bool CSRecordSet::IsMemoFileTable(void){
    return ((CXdbRecordset*)pObject)->IsMemoFileTable();
}
bool CSRecordSet::IsNumberField(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->IsNumberField(nFieldIndex);
}
bool CSRecordSet::GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer){
    return ((CXdbRecordset*)pObject)->GetFieldValueIntoBuffer(nFieldIndex,pbuffer);
}
void CSRecordSet::Edit(void){
    ((CXdbRecordset*)pObject)->Edit();
}
bool CSRecordSet::Update(void){
    return ((CXdbRecordset*)pObject)->Update();
}
bool CSRecordSet::SetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->SetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer){
    return ((CXdbRecordset*)pObject)->SetFieldValueFromBuffer(nFieldIndex,pbuffer);
}
bool CSRecordSet::GetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->GetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SeekRecord(XSEK *SEK){
    return ((CXdbRecordset*)pObject)->TableSeek(SEK);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,char *key, int length){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,key,length);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,long i){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,i);
}
bool CSRecordSet::SeekRecord(int nFieldIndex, double d, int decimals)
{
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,d,decimals);
}
int CSRecordSet::GetRecordLength(void){
    return ((CXdbRecordset*)pObject)->GetRecordLength();
}
char *CSRecordSet::GetMemoField(int nMemoFieldIndex,char *pbuffer, int BUFFER_SIZE){
    return ((CXdbRecordset*)pObject)->GetMemoField(nMemoFieldIndex,pbuffer,BUFFER_SIZE);
}
bool CSRecordSet::GetBinaryField(unsigned char *buffer,long *buf_size){
    return ((CXdbRecordset*)pObject)->GetBinaryField(buffer,buf_size);
}
LPXSQL CSRecordSet::GetSQL(void){
    return ((CXdbRecordset*)pObject)->GetSQL();
}
void CSRecordSet::GoToLast(void){
    ((CXdbRecordset*)pObject)->GoToLast();
}
long CSRecordSet::GetRecordID(void){
    return ((CXdbRecordset*)pObject)->GetRecordID();
}
int CSRecordSet::GetStandardFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetStandardFieldCount();
}
int CSRecordSet::GetTableType(void){
    return ((CXdbRecordset*)pObject)->GetTableType();
}
int CSRecordSet::GetFieldType(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldType(nFieldIndex);
}
int CSRecordSet::GetFieldDGSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldDGSize(nFieldIndex);
}
int CSRecordSet::GetFieldSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldSize(nFieldIndex);
}

EDYCJA: wymagane przez DeadMG:

void nimportequoidumomentquecaroule(void) {

    short i = -4;
    unsigned short j=(unsigned short)i;

}

1
Ten opis może zostać znacznie ulepszony przez jakiś kod ilustrujący to, co opisujesz. Mam wrażenie, że dotyczyło to pierwotnego pytania, ale jeśli ostrzegłbyś nas przed niebezpieczeństwem w tym scenariuszu, pomogłoby to rozwinąć temat pytającego.
DeveloperDon

1
Ta obsada DWORDjest obraźliwa i być może niepoprawna (DWORD niekoniecznie jest wystarczająco szeroki, aby pomieścić wskaźnik). Jeśli potrzebujesz nietypowego wskaźnika, skorzystaj void*- ale kiedy potrzebujesz go w C ++, często masz problem z projektem w kodzie, który powinieneś naprawić.
Mat

Salvador, myślę, że próbujesz powiedzieć coś o VC6 i tym, jak ma niezwykłą i nieoczekiwaną obsługę wskaźnika. W przykładzie mogą skorzystać komentarze, a jeśli widzisz ostrzeżenia z kompilatora, mogą one być pouczające w odniesieniu do powiązania twojej odpowiedzi z pytaniem.
DeveloperDon

@Mat Ten przykład dotyczy 32-bitowego systemu operacyjnego i kompilatora VC6 ++.
Salvador,

6
Mówisz o złym kodzie w absolutnie starożytnym kompilatorze? Nie, dziękuję.
DeadMG
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.