Por que os ponteiros não são recomendados ao codificar com C ++?

42

Eu li em algum lugar que, ao usar o C ++, é recomendado não usar ponteiros. Por que os ponteiros são uma idéia tão ruim quando você está usando o C ++. Para programadores C que estão acostumados a usar ponteiros, qual é a melhor alternativa e abordagem em C ++?

    
por Joshua Partogi 11.03.2011 / 07:50
fonte

9 respostas

57

Acho que eles significam que você deve usar ponteiros inteligentes em vez de ponteiros regulares.

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.

Em C ++, a ênfase estaria na coleta de lixo e na prevenção de vazamentos de memória (apenas para citar dois). Os ponteiros são uma parte fundamental da linguagem, portanto, não usá-los é praticamente impossível, exceto nos programas mais complicados.

    
por 11.03.2011 / 07:59
fonte
92

Desde que eu sou o único que publicou a polêmica "não use os ponteiros f * cking" Eu sinto que devo comentar aqui.

Em primeiro lugar, como polêmica, obviamente, representa um ponto de vista extremo. Existem definitivamente usos legítimos de ponteiros (brutos). Mas eu (e muitos programadores C ++ profissionais) sustentam que esses casos são extremamente raros. Mas o que realmente queremos dizer é o seguinte:

Primeiro:

Raw pointers must under no circumstances own memory.

Aqui, "memória própria" significa essencialmente que em algum momento delete é chamado nesse ponteiro (mas é mais geral que isso). Esta declaração pode ser considerada como absoluta. A exceção somente é ao implementar seu próprio ponteiro inteligente (ou outra estratégia de gerenciamento de memória). E mesmo lá você normalmente deveria ainda usar um ponteiro inteligente em baixo nível.

A justificativa para isso é bem simples: ponteiros brutos que possuem memória introduzem uma fonte de erro. E esses erros são prolíficos no software existente: vazamentos de memória e exclusão dupla - ambos uma consequência direta da falta de clareza na propriedade dos recursos (mas indo na direção oposta).

Esse problema pode ser totalmente eliminado, virtualmente sem nenhum custo, simplesmente usando ponteiros inteligentes em vez de ponteiros brutos (advertência: isso ainda requer pensar, é claro; ponteiros compartilhados podem levar a ciclos e, assim, mais uma vez a vazamentos de memória - mas isso é facilmente evitável).

Segundo:

Most uses of pointers in C++ are unnecessary.

Ao contrário de outras linguagens, o C ++ tem um suporte muito strong para a semântica de valor e simplesmente não precisa da indireção de ponteiros. Isso não foi imediatamente percebido - historicamente, o C ++ foi inventado para facilitar a fácil orientação a objetos em C e se baseou strongmente na construção de gráficos de objetos conectados por ponteiros. Mas no C ++ moderno, esse paradigma raramente é a melhor opção, e os idiomas modernos do C ++ geralmente não precisam de ponteiros . Eles operam em valores em vez de ponteiros.

Infelizmente, essa mensagem ainda não foi capturada em grandes partes da comunidade de usuários do C ++. Como resultado, a maior parte do código C ++ que está escrito ainda está repleta de ponteiros supérfluos que tornam o código complexo, lento e defeituoso / não confiável.

Para alguém que conhece o C ++ moderno, fica claro que você raramente precisa de quaisquer ponteiros (inteligentes ou crus; exceto quando usados como iteradores). O código resultante é mais curto, menos complexo, mais legível, muitas vezes mais eficiente e mais confiável.

    
por 01.09.2012 / 15:20
fonte
15

Simplesmente porque há abstrações disponíveis para o seu, que ocultam os aspectos mais temperamentais do uso de ponteiros, como o acesso à memória bruta e a limpeza após as alocações. Com ponteiros inteligentes, classes de contêineres e padrões de design como RAII, a necessidade de usar ponteiros brutos é diminuída. Dito isso, como qualquer abstração, você deve entender como eles realmente funcionam antes de ir além deles.

    
por 11.03.2011 / 08:00
fonte
10

Uma das razões é a aplicação muito ampla de ponteiros. Eles podem ser usados para iteração sobre contêineres, para evitar a cópia de objetos grandes ao passar para a função, gerenciamento não-trivial do tempo de vida, acesso a locais aleatórios na memória etc. E quando você os usa para uma finalidade, outros recursos ficam disponíveis imediatamente de forma independente na intenção.

A seleção de uma ferramenta para fins exatos torna o código mais simples e a intenção mais visível - iteradores para iterações, ponteiros inteligentes para gerenciamento de tempo de vida, etc.

    
por 11.03.2011 / 08:11
fonte
10

De maneira relativamente simples, a mentalidade C é "Tem um problema? Use um ponteiro". Você pode ver isso em strings C, ponteiros de função, ponteiros como iteradores, ponteiro para ponteiro, ponteiro nulo - mesmo nos primeiros dias de C ++ com ponteiros de membros.

Mas em C ++ você pode usar valores para muitas ou todas essas tarefas. Precisa de uma abstração de função? %código%. É um valor que é uma função. %código%? É um valor, isso é uma string. Você pode ver abordagens semelhantes em todo o C ++. Isso torna a análise do código muito mais fácil para humanos e compiladores.

    
por 01.09.2012 / 15:52
fonte
3

Além das razões já listadas, há uma óbvia: melhores otimizações. A análise de aliasing é complicada demais na presença de uma aritmética de ponteiro, enquanto as referências sugerem um otimizador, portanto, uma análise de aliasing muito mais profunda é possível se apenas referências forem usadas.

    
por 11.03.2011 / 11:20
fonte
2

Além do risco de vazamentos de memória declarados por @jmquigley, ponteiro e aritmética de ponteiro podem ser considerados problemáticos porque ponteiros podem apontar para toda a memória causando "bugs difíceis de encontrar" e "vulnerabilidades de segurança".

É por isso que eles foram quase abandonados em C # e Java.

    
por 11.03.2011 / 08:49
fonte
-1

C ++ suporta a maioria dos C , recursos, além de objetos e classes. C já tinha ponteiros e outras coisas.

Os ponteiros são uma técnica muito útil, que pode ser combinada com a orientação a objetos, e o C ++ os suporta. Mas essa técnica é difícil de ensinar e difícil de entender e é muito fácil causar erros indesejáveis.

Muitas novas linguagens de programação fingem não usar ponteiros com objetos, como Java, .NET, Delphi, Vala, PHP, Scala. Mas, os ponteiros ainda são usados, "nos bastidores". Essas técnicas de "ponteiro oculto" são chamadas de "referências".

De qualquer forma, considero o ponteiro (s) como um Padrão de Programação, como uma maneira válida de resolver certos problemas, assim como a Programação Orientada a Objetos .

Outros desenvolvedores podem ter uma opinião diferente. Mas sugiro que alunos e programadores aprendam como:

(1) Use ponteiros sem objetos

(2) objetos sem ponteiros

(3) ponteiros explícitos para objetos

(4) ponteiros "ocultos" para objetos (A.K.A. referência ); -)

nessa ordem.

Mesmo que seja difícil de ensinar e difícil de aprender. Object Pascal (Delphi, FreePascal, outros) e C++ (não Java ou C #) podem ser usados para esses objetivos.

E, mais tarde, programadores novatos, podem passar para linguagens de programação "ponteiros ocultos para objetos" como: Java, C #, PHP Orientado a Objetos e outros.

    
por 11.03.2011 / 18:28
fonte
-6

Falando sobre VC6, quando você lança um ponteiro de uma classe (que você instancia) em uma variável (por exemplo, DWORD), mesmo se esse ponteiro é local, você pode acessar a classe sobre todas as funções que usam o mesmo heap. A classe instanciada é definida como local, mas na verdade não é. Tanto quanto eu sei, qualquer endereço de uma variável de heap, estrutura ou classe é único ao longo de toda a vida da classe de hospedagem.

Exemplo:

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 Isso é uma parte muito pequena do código original. A classe CSRecodset é apenas uma classe de conversão de CXdbRecordset, onde todo o código real é. Ao fazer isso, posso deixar que o usuário aproveite o que eu escrevi sem perder meus direitos. Eu não pretendo demonstrar que meu mecanismo de banco de dados é profissional, mas realmente funciona.

//-------------------------------------
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: solicitado pela DeadMG:

void nimportequoidumomentquecaroule(void) {

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

}
    
por 20.08.2012 / 19:50
fonte

Tags