Leí en algún lugar que al usar C ++ se recomienda no usar punteros. ¿Por qué los punteros son una mala idea cuando usas C ++? Para los programadores de C que están acostumbrados a usar punteros, ¿cuál es la mejor alternativa y enfoque en C ++?
Leí en algún lugar que al usar C ++ se recomienda no usar punteros. ¿Por qué los punteros son una mala idea cuando usas C ++? Para los programadores de C que están acostumbrados a usar punteros, ¿cuál es la mejor alternativa y enfoque en C ++?
Creo que quieren decir que debes usar punteros inteligentes en lugar de los punteros regulares.
En informática, un puntero inteligente. es un tipo de datos abstracto que simula un puntero mientras proporciona características adicionales, tales como automática Recolección de basura o control de límites. Estas características adicionales están destinadas para reducir los errores causados por el mal uso de punteros manteniendo la eficiencia. Los punteros inteligentes normalmente hacen un seguimiento de los objetos que apuntan para el propósito de la gestión de la memoria.
El mal uso de los punteros es un importante fuente de errores: la constante asignación, desasignación y referencia que debe ser realizada por un programa escrito utilizando punteros introduce el riesgo de que la memoria se escape ocurrira. Punteros inteligentes tratan de evitar las fugas de memoria haciendo que el desasignación automática de recursos: cuando El puntero (o el último de una serie. de punteros) a un objeto es Destruido, por ejemplo, porque va. Fuera de alcance, el objeto puntiagudo es destruido también.
En C ++, el énfasis estaría en la recolección de basura y la prevención de pérdidas de memoria (solo para nombrar dos). Los punteros son una parte fundamental del lenguaje, por lo que no usarlos es prácticamente imposible, excepto en la mayoría de los programas.
Ya que soy el que publicó la polémica "no use f * cking pointers" Siento que debería comentar aquí.
En primer lugar, como una polémica, obviamente representa un punto de vista extremo. Hay son usos legítimos de los punteros (sin formato). Pero yo (y muchos programadores profesionales de C ++) sostengo que estos casos son extremadamente raros. Pero lo que realmente queremos decir es lo siguiente:
Los punteros sin formato no deben en ningún caso poseer memoria.
Aquí, "memoria propia" significa esencialmente que en algún punto se llama a delete
en ese puntero (pero es más general que eso). Esta declaración se puede tomar de forma segura como absoluta. La excepción only es cuando se implementa su propio puntero inteligente (u otra estrategia de administración de memoria). E incluso allí, normalmente debería todavía usar un puntero inteligente en el nivel bajo.
La razón de esto es bastante simple: los punteros en bruto que poseen memoria introducen una fuente de error. Y estos errores son prolíficos en el software existente: pérdidas de memoria y eliminación doble, ambas una consecuencia directa de la propiedad poco clara de los recursos (pero en dirección opuesta).
Este problema se puede eliminar por completo, prácticamente sin costo, simplemente usando punteros inteligentes en lugar de punteros sin procesar (advertencia: esto todavía requiere reflexión, por supuesto; los punteros compartidos pueden conducir a ciclos y, por lo tanto, una vez más a las fugas de memoria, pero esto es fácilmente evitable).
La mayoría de los usos de los punteros en C ++ son innecesarios.
A diferencia de otros idiomas, C ++ tiene un soporte muy fuerte para la semántica de valores y simplemente no necesita la dirección de punteros. Esto no se realizó de inmediato: históricamente, C ++ se inventó para facilitar la fácil orientación del objeto en C, y se basó en gran medida en la construcción de gráficos de objetos que estaban conectados mediante punteros. Pero en C ++ moderno, este paradigma rara vez es la mejor opción, y los modismos C ++ modernos a menudo no necesitan punteros . Operan en valores en lugar de punteros.
Lamentablemente, este mensaje aún no se ha detectado en gran parte de la comunidad de usuarios de C ++. Como resultado, la mayoría del código C ++ que se escribe todavía está lleno de punteros superfluos que hacen que el código sea complejo, lento y defectuoso / no confiable.
Para alguien que conoce C ++ moderno, está claro que rara vez se necesitan cualquier puntero (ya sea inteligente o sin formato, excepto cuando se usan como iteradores). El código resultante es más corto, menos complejo, más legible, a menudo más eficiente y más confiable.
Simplemente porque hay abstracciones disponibles que ocultan los aspectos más temperamentales del uso de los punteros, como el acceso a la memoria en bruto y la limpieza después de las asignaciones. Con los punteros inteligentes, las clases de contenedor y los patrones de diseño como RAII, la necesidad de usar punteros sin procesar disminuye. Dicho esto, como cualquier abstracción, debes entender cómo funcionan realmente antes de ir más allá de ellas.
Una de las razones es la aplicación demasiado amplia de los punteros. Se pueden usar para la iteración en contenedores, para evitar copiar objetos grandes al pasar a la función, administración no trivial de por vida, acceso a lugares aleatorios en la memoria, etc. Y una vez que los usó para un propósito, otras funciones están disponibles. inmediatamente de forma independiente en la intención.
La selección de una herramienta para un propósito exacto hace que el código sea más simple y más visible: iteradores para iteraciones, punteros inteligentes para la administración de por vida, etc.
Relativamente simple, la mentalidad de C es "¿Tienes un problema? Usa un puntero". Puede ver esto en C cadenas, punteros de función, punteros como iteradores, puntero a puntero, puntero de vacío, incluso en los primeros días de C ++ con punteros de miembro.
Pero en C ++ puede usar valores para muchas o todas estas tareas. ¿Necesita una función de abstracción? %código%. Es un valor que es una función. %código%? Es un valor, eso es una cadena. Puedes ver enfoques similares en todo C ++. Esto hace que el análisis del código sea mucho más fácil tanto para los humanos como para los compiladores.
Además de las razones ya enumeradas, hay una obvia: mejores optimizaciones. El análisis de alias es demasiado complicado en presencia de una aritmética de punteros, mientras que las referencias sugieren un optimizador, por lo que es posible un análisis de alias mucho más profundo si solo se usan referencias.
Además del riesgo de pérdidas de memoria indicadas por @jmquigley, el puntero y la aritmética de punteros pueden considerarse problemáticos porque los punteros pueden apuntar a cualquier parte de la memoria causando "errores difíciles de encontrar" y "vulnerabilidades de seguridad".
Es por eso que casi fueron abandonados en C # y Java.
C ++ es compatible con la mayoría de C , características, además de Objetos y Clases. C ya tenía punteros y otras cosas.
Los punteros son una técnica muy útil, que se puede combinar con la orientación de objetos, y C ++ los admite. Pero, esta técnica es difícil de enseñar y difícil de entender, y es muy fácil causar errores no deseados.
Muchos nuevos lenguajes de programación pretenden no usar punteros con objetos, como Java, .NET, Delphi, Vala, PHP, Scala. Pero, los punteros todavía se utilizan, "detrás de las escenas". Estas técnicas de "puntero oculto" se denominan "referencias".
De todos modos, considero puntero (s) como un Patrón de Programación, como una forma válida de resolver ciertos problemas, así como Programación Orientada a Objetos sí lo hace.
Otros desarrolladores pueden tener una opinión diferente. Pero, sugiero que los estudiantes y los programadores aprendan a:
(1) Use punteros sin objetos
(2) objetos sin punteros
(3) punteros explícitos a objetos
(4) punteros "ocultos" a objetos (A.K.A. referencia ) ;-)
En ese orden.
Incluso si es difícil de enseñar, y difícil de aprender. Object Pascal (Delphi, FreePascal, otros) y C++
(no Java o C #) se pueden usar para esos objetivos.
Y, más tarde, los programadores novatos, pueden pasar a lenguajes de programación de "punteros ocultos a objetos" como: Java, C #, PHP orientado a objetos y otros.
Hablando de VC6, cuando lanza un puntero de una clase (que instancia) en una variable (por ejemplo, DWORD), incluso si este puntero es local, puede acceder a la clase sobre todas las funciones que usan el mismo montón. La clase instanciada se define como local, pero en realidad no lo es. Por lo que sé, cualquier dirección de una variable, estructura o clase de pila es única a lo largo de toda la vida de la clase de alojamiento.
Ejemplo:
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;
}
EDITAR Eso es una parte muy pequeña del código original. La clase CSRecodset es solo una clase de conversión de CXdbRecordset, donde está todo el código real. Al hacerlo, puedo permitir que el usuario se beneficie de lo que escribí sin perder mis derechos. No pretendo demostrar que mi motor de base de datos es profesional, pero 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);
}
EDITAR: solicitado por DeadMG:
void nimportequoidumomentquecaroule(void) {
short i = -4;
unsigned short j=(unsigned short)i;
}