Perché i puntatori non sono raccomandati durante la codifica con C ++?

42

Ho letto da qualche parte che quando si usa il C ++ si raccomanda di non usare i puntatori. Perché i puntatori sono una cattiva idea quando si usa il C ++. Per i programmatori C abituati a usare i puntatori, qual è l'alternativa e l'approccio migliori in C ++?

    
posta Joshua Partogi 11.03.2011 - 07:50
fonte

9 risposte

57

Penso che significhino che dovresti usare puntatori intelligenti invece di puntatori regolari.

In computer science, a smart pointer is an abstract data type that simulates a pointer while providing additional features, such as automatic garbage collection or bounds checking. These additional features are intended to reduce bugs caused by the misuse of pointers while retaining efficiency. Smart pointers typically keep track of the objects they point to for the purpose of memory management.

The misuse of pointers is a major source of bugs: the constant allocation, deallocation and referencing that must be performed by a program written using pointers introduces the risk that memory leaks will occur. Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer (or the last in a series of pointers) to an object is destroyed, for example because it goes out of scope, the pointed object is destroyed too.

In C ++ l'enfasi sarebbe sulla raccolta dei rifiuti e sulla prevenzione delle perdite di memoria (solo per citarne due). I puntatori sono una parte fondamentale del linguaggio, quindi non usarli è praticamente impossibile tranne nel più trival dei programmi.

    
risposta data 11.03.2011 - 07:59
fonte
92

Poiché io sono colui che ha pubblicato la polemica "non utilizzare f * cking pointers" Sento che dovrei commentare qui.

Innanzi tutto, in quanto polemica rappresenta ovviamente un punto di vista estremo. Ci sono usi decisamente legittimi dei puntatori (grezzi). Ma io (e molti programmatori C ++ professionali) sostengo che questi casi sono estremamente rari. Ma ciò che intendiamo veramente è il seguente:

Primo:

Raw pointers must under no circumstances own memory.

Qui, "memoria propria" significa essenzialmente che ad un certo punto% viene chiamatodelete su quel puntatore (ma è più generale di quello). Questa affermazione può essere considerata come assoluta. L'eccezione solo è quando si implementa il proprio puntatore intelligente (o altra strategia di gestione della memoria). E anche lì dovresti normalmente ancora usare un puntatore intelligente a basso livello.

La logica di questo è abbastanza semplice: i puntatori grezzi che possiedono la memoria introducono una fonte di errore. E questi errori sono prolifici nel software esistente: perdite di memoria e doppia cancellazione, entrambe conseguenze dirette della proprietà delle risorse non chiara (ma andando nella direzione opposta).

Questo problema può essere completamente eliminato, praticamente senza alcun costo, semplicemente usando puntatori intelligenti invece di puntatori grezzi (avvertenza: questo richiede ancora di pensare, naturalmente, i puntatori condivisi possono portare a cicli e quindi ancora una volta a perdite di memoria - ma questo è facilmente evitabile).

Secondo:

Most uses of pointers in C++ are unnecessary.

A differenza di altri linguaggi, C ++ ha un supporto molto strong per la semantica del valore e semplicemente non ha bisogno di riferimenti indiretti. Questo non è stato immediatamente realizzato - storicamente, il C ++ è stato inventato per facilitare l'orientamento degli oggetti in C, e si basava molto sulla costruzione di grafici di oggetti che erano collegati da puntatori. Ma nel moderno C ++, questo paradigma è raramente la scelta migliore, e gli idiomi C ++ moderni spesso non hanno bisogno di alcun suggerimento . Funzionano su valori anziché puntatori.

Sfortunatamente, questo messaggio non è ancora presente in gran parte della comunità di utenti C ++. Di conseguenza, la maggior parte del codice C ++ scritto è ancora disseminato di puntatori superflui che rendono il codice complesso, lento e difettoso / inaffidabile.

Per qualcuno che conosce il C ++ moderno, è chiaro che raramente hai bisogno di qualsiasi puntatore (intelligente o grezzo, eccetto quando li usi come iteratori). Il codice risultante è più breve, meno complesso, più leggibile, spesso più efficiente e più affidabile.

    
risposta data 01.09.2012 - 15:20
fonte
15

Semplicemente perché sono disponibili astrazioni per nascondere gli aspetti più caratteristici dell'utilizzo dei puntatori, come l'accesso alla memoria grezza e la pulizia dopo le allocazioni. Con puntatori intelligenti, classi di contenitori e modelli di progettazione come RAII, la necessità di utilizzare puntatori grezzi è ridotta. Detto questo, come ogni astrazione, dovresti capire come funzionano effettivamente prima di andare oltre.

    
risposta data 11.03.2011 - 08:00
fonte
10

Una delle ragioni è l'applicazione troppo ampia di indicatori. Possono essere utilizzati per l'iterazione sui contenitori, per evitare la copia di oggetti di grandi dimensioni quando si passa alla funzione, la gestione della vita non banale, l'accesso a posizioni casuali in memoria, ecc. E una volta utilizzati per uno scopo, diventano disponibili altre funzionalità immediatamente indipendentemente dall'intenzione.

La selezione di uno strumento per scopi precisi rende il codice più semplice e l'intento più visibile - iteratori per iterazioni, puntatori intelligenti per la gestione della vita, ecc.

    
risposta data 11.03.2011 - 08:11
fonte
10

Relativamente semplicemente, la mentalità C è "Hai un problema? Usa un puntatore". Puoi vederlo in stringhe C, puntatori di funzioni, puntatori-come-iteratori, puntatore-puntatore, puntatore vuoto, anche nei primi giorni del C ++ con i puntatori membri.

Ma in C ++ puoi usare i valori per molte o tutte queste attività. Hai bisogno di un'astrazione di funzioni? %codice%. È un valore che è una funzione. %codice%? È un valore, è una stringa. Puoi vedere approcci simili su tutto il C ++. Ciò rende l'analisi del codice molto più semplice sia per gli utenti che per i compilatori.

    
risposta data 01.09.2012 - 15:52
fonte
3

Oltre alle ragioni già elencate, c'è una ovvia: migliori ottimizzazioni. L'analisi dell'aliasing è troppo complicata per la presenza di un'aritmetica puntatore, mentre i riferimenti suggeriscono un ottimizzatore, quindi un'analisi di aliasing molto più profonda è possibile se vengono utilizzati solo riferimenti.

    
risposta data 11.03.2011 - 11:20
fonte
2

Oltre al rischio di perdite di memoria dichiarate da @jmquigley, il puntatore e l'aritmetica del puntatore possono essere considerati problematici perché i puntatori possono puntare ovunque nella memoria causando "errori di difficile individuazione" e "vulnerabilità di sicurezza".

Ecco perché sono stati quasi abbandonati in C # e Java.

    
risposta data 11.03.2011 - 08:49
fonte
-1

C ++ supporta la maggior parte di C , funzioni, oltre a oggetti e classi. C conteneva già puntatori e altre cose.

I puntatori sono una tecnica molto utile, che può essere combinata con Orientamento oggetto e C ++ li supporta. Ma questa tecnica è difficile da insegnare e difficile da capire, ed è molto facile causare errori indesiderati.

Molti nuovi linguaggi di programmazione fanno finta di non usare puntatori con oggetti come Java, .NET, Delphi, Vala, PHP, Scala. Ma i puntatori sono ancora usati, "dietro le quinte". Queste tecniche di "puntatori nascosti" sono chiamate "riferimenti".

Comunque, considero il puntatore (s) come un Pattern di programmazione, come un modo valido per risolvere determinati problemi, così come Programmazione orientata agli oggetti .

Altri sviluppatori potrebbero avere un'opinione diversa. Ma, suggerisco agli studenti e ai programmatori di imparare come:

(1) Usa puntatori senza oggetti

(2) oggetti senza puntatori

(3) puntatori espliciti agli oggetti

(4) puntatori "nascosti" agli oggetti (A.K.A. reference ); -)

In questo ordine.

Anche se è difficile da insegnare e difficile da imparare. Object Pascal (Delphi, FreePascal, altri) e C++ (non Java o C #) possono essere utilizzati per tali obiettivi.

E, in seguito, i programmatori alle prime armi, possono passare a linguaggi di programmazione "puntatori nascosti agli oggetti" come: Java, C #, PHP orientato agli oggetti e altri.

    
risposta data 11.03.2011 - 18:28
fonte
-6

Parlando di VC6, quando lanci un puntatore di una classe (che instanziati) in una variabile (ad esempio DWORD), anche se questo puntatore è locale puoi accedere alla classe su tutte le funzioni che usano lo stesso heap. La classe istanziata è definita come locale ma in realtà non lo è. Per quanto ne so, qualsiasi indirizzo di una variabile, struttura o classe heap è unico per tutta la durata della classe di hosting.

Esempio:

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;
}

EDIT Questa è una parte molto piccola del codice originale. La classe CSRecodset è solo una classe di casting di CXdbRecordset, in cui tutto il codice reale è. Così facendo posso lasciare che l'utente tragga beneficio da ciò che ho scritto senza perdere i miei diritti. Non pretendo di dimostrare che il mio motore di database è professionale ma funziona davvero.

//-------------------------------------
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);
}

EDIT: richiesto da DeadMG:

void nimportequoidumomentquecaroule(void) {

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

}
    
risposta data 20.08.2012 - 19:50
fonte

Leggi altre domande sui tag