C ++로 코딩 할 때 포인터가 권장되지 않는 이유는 무엇입니까?


45

C ++을 사용할 때 포인터를 사용하지 않는 것이 좋습니다. C ++을 사용할 때 포인터가 왜 그렇게 나쁜 생각입니까? 포인터를 사용하는 데 익숙한 C 프로그래머의 경우 C ++에서 더 나은 대안과 접근 방법은 무엇입니까?


40
"어딘가에"링크하십시오. 상황이 매우 관련이있을 수 있습니다.

1
이 질문 은 희망적으로 당신에게 유용합니다.
Garet Claborn

이러한 답변의 대부분은 주요 원인으로 메모리 누수를 피하는 것을 말합니다. 포인터를 사용하더라도 앱 중 하나에서 메모리 누수 문제가 발생한 마지막 시간을 기억할 수 없습니다. 메모리 누수 문제가있는 경우 올바른 도구를 사용하지 않거나 수행중인 작업을 모르는 것입니다. 대부분의 개발 환경에는 누출이 자동으로 확인되는 방법이 있습니다. 메모리 누수 문제는 발생 빈도가 더 미묘하고 범인을 추적하기 위해 종종 타사 도구가 필요하기 때문에 가비지 수집 언어에서 추적하기가 훨씬 더 어렵다고 생각합니다 .
덩크

1
@Dunk의 의견에 덧붙여 때로는 고급 언어의 내장 가비지 수집기가 제대로 작동하지 않습니다. 예를 들어 ActionScript 3의 가비지 수집기는 그렇지 않습니다. 현재 NetConnection서버에서 연결이 끊긴 인스턴스 ( stackoverflow.com/questions/14780456/… ) 와 관련이있는 버그가 있으며 프로그램에 여러 객체가있어서 수집을 거부하는 문제가 있습니다. ...
Panzercrisis

... ( adobe.com/devnet/actionscript/learning/as3-fundamentals/…-로GCRoots are never garbage collected. 시작하여 단락을 검색 The MMgc is considered a conservative collector for mark/sweep.) 기술적으로 이것은 AS3 자체가 아닌 Adobe Virtual Machine 2의 문제이지만 가비지 수집 기능이 기본적으로 제공되는 고급 언어에서는 이와 같은 문제가 발생하는 경우 종종 언어 내에서 디버깅 할 수있는 진정한 방법이 없습니다. 이러한 문제는 프로그램에서 완전히 벗어납니다. ...
Panzercrisis

답변:


58

일반 포인터 대신 스마트 포인터 를 사용해야한다는 의미 입니다.

컴퓨터 과학에서 스마트 포인터는 자동 가비지 수집 또는 범위 검사와 같은 추가 기능을 제공하면서 포인터를 시뮬레이션하는 추상 데이터 형식입니다. 이러한 추가 기능은 포인터를 잘못 사용하여 발생하는 버그를 줄이고 효율성을 유지하기위한 것입니다. 스마트 포인터는 일반적으로 메모리 관리를 위해 대상이 가리키는 객체를 추적합니다.

포인터의 오용은 버그의 주요 원인입니다. 포인터를 사용하여 작성된 프로그램이 수행해야하는 지속적인 할당, 할당 해제 및 참조는 메모리 누수가 발생할 위험을 초래합니다. 스마트 포인터는 리소스 할당을 자동으로 만들어 메모리 누수를 방지하려고합니다. 예를 들어 범위를 벗어나서 개체에 대한 포인터 (또는 일련의 포인터 중 마지막 포인터)가 소멸되면 지정된 개체도 소멸됩니다.

C ++에서는 가비지 수집과 메모리 누수 방지에 중점을 둡니다 (두 이름 만 지정). 포인터는 언어의 기본 부분이므로, 가장 부족한 프로그램을 제외하고는 사용하지 않는 것이 거의 불가능합니다.


22
일반적으로 고전적인 의미에서 엄격하게 가비지 수집되지는 않으며 더 많은 참조 계산이 가능합니다. 적어도 나는 똑똑한 ptr에서 ([boost | std] :: shared_ptr)
Doug T.

3
이 답변은 스마트 포인터에만 국한되며 문제의 작은 부분에 지나지 않습니다. 또한 스마트 포인터는 가비지 수집이 아닙니다.
deadalnix 2016 년

3
또한 일반적으로 RAII.
클라 임

3
아니요, 이것은 의미가 아닙니다. 그것은 하나의 양상일 뿐이며 가장 중요한 것은 아니다.
Konrad Rudolph

나는 똑똑한 포인터가 가장 중요한 측면이라고 생각하지만 다른 것들도 많다는 데 동의합니다.
DeadMG

97

나는 “f * cking 포인터를 사용하지 마십시오” 라는 논쟁을 출판 한 사람이기 때문에 여기서 의견을 말해야한다고 생각합니다.

우선, 그것은 논쟁의 여지가 극단적 인 관점을 분명히 나타냅니다. 이 있습니다 (원시) 포인터의 합법적 인 사용은 확실히. 그러나 나는 (그리고 많은 전문 C ++ 프로그래머들)이 경우는 매우 드물다고 주장합니다. 그러나 우리가 실제로 의미하는 것은 다음과 같습니다.

먼저:

원시 포인터는 어떤 경우에도 메모리를 소유하지 않아야합니다.

여기서 "자신의 메모리"는 본질적으로 어떤 시점 delete에서 해당 포인터에서 호출 된다는 것을 의미합니다 (그러나 그보다 더 일반적입니다). 이 진술은 절대적으로 안전하게 취할 수 있습니다. 단지 자신의 스마트 포인터 (또는 다른 메모리 관리 전략)을 구현하는 경우는 예외입니다. 심지어 거기에서도 일반적으로 여전히 낮은 수준에서 스마트 포인터를 사용해야합니다.

이에 대한 이론적 근거는 매우 간단합니다. 메모리를 소유 한 원시 포인터는 오류의 원인이됩니다. 그리고 이러한 오류는 기존 소프트웨어에서 메모리 누출 및 이중 삭제 – 다원 한 자원 소유권의 직접적인 결과 (그러나 반대 방향으로 진행)에서 많이 발생합니다.

이 문제는 전적으로 단순히 스마트 포인터 대신 원시 포인터를 사용하여, 거의 무료로, 제거 할 수있다 (주의 :이 여전히 물론 사고, 요구, 공유 포인터가 있습니다 메모리 누수에 다시 한 번 따라서 사이클 이어질를 - 그러나 이것은 쉽게입니다 피할 수 있음).

둘째:

C ++에서 대부분의 포인터 사용은 필요하지 않습니다.

다른 언어와 달리 C ++은 값 의미론을 매우 강력하게 지원하므로 포인터의 간접적 인 지시가 필요하지 않습니다. 이것은 즉시 실현되지 않았습니다. 역사적으로 C ++은 C에서 객체 지향을 용이하게하기 위해 발명되었으며 포인터로 연결된 객체 그래프를 만드는 데 크게 의존했습니다. 그러나 현대 C ++에서는이 패러다임이 최선의 선택 이 아니며 현대 C ++ 관용구에는 종종 포인터가 필요하지 않습니다 . 포인터가 아닌 값에서 작동합니다 .

불행히도이 메시지는 여전히 C ++ 사용자 커뮤니티의 많은 부분에서 포착되지 않았습니다. 결과적으로 작성된 대부분의 C ++ 코드는 여전히 복잡하고 느리고 결함이 있거나 신뢰할 수없는 불필요한 포인터로 가득 차 있습니다.

현대적인 C ++을 알고있는 사람에게는 포인터가 거의 필요 하지 않다는 것이 분명합니다 (이터레이터로 사용할 때를 제외하고는 스마트하거나 원시). 결과 코드는 더 짧고, 복잡하지 않고, 더 읽기 쉽고, 종종 더 효율적이고 신뢰할 수 있습니다.


5
한숨 ... 이것은 정말로 30 개 이상의 공감대에 대한 답이어야합니다. 현대 C ++ 에서는 포인터가 거의 필요 하지 않습니다 .
Charles Salvia

1
예를 들어. Gui 객체는 많은 doc 객체를 소유합니다. 이것들은 포인터로 가지고있어 클래스를 앞으로 선언 할 수 있고 (재 컴파일을 피할 수 있습니다) 스택에 빈 상태로 생성 된 다음 나중에 파일을 생성하지 않고 (새로) 객체를 초기화 할 수 있습니까? 이것은 포인터를 완벽하게 유효하게 사용하는 것처럼 보입니다. 캡슐화 된 모든 객체가 힙에 있지 않아야합니까?
Martin Beckett

4
@Martin GUI 객체는 포인터 객체 그래프가 최상의 솔루션 인 경우입니다. 그러나 메모리 소유의 원시 포인터에 대한 칙령은 여전히 ​​유효합니다. 전체적으로 공유 포인터를 사용하거나 적절한 소유권 모델을 개발하고 원시 포인터를 통해 컨트롤간에 약한 (= 비 소유) 관계를 갖습니다.
Konrad Rudolph

1
@ VF1 std::unique_ptr. 또한 왜 안 ptr_vec됩니까? 그러나 일반적으로 값 벡터는 여전히 더 빨리 스왑됩니다 (특히 이동 의미론).
Konrad Rudolph

1
@gaazkam 아무도 표준 용기 만 사용하도록 강요하지 않았습니다. 예를 들어 Boost는 불완전한 유형을 지원하는 맵 구현을 가지고 있습니다. 또 다른 해결책은 유형 삭제를 사용하는 것입니다. boost::variant로모그래퍼 recursive_wrapper아마 DAG를 대표하는 내가 좋아하는 솔루션입니다.
Konrad Rudolph

15

원시 메모리에 대한 액세스 및 할당 후 정리와 같이 포인터를 사용하는 것과 같은 더 일시적인 측면을 숨길 수있는 추상화가 있기 때문입니다. 스마트 포인터, 컨테이너 클래스 및 RAII와 같은 디자인 패턴을 사용하면 원시 포인터를 사용할 필요성이 줄어 듭니다. 즉, 어떤 추상화와 마찬가지로, 그것들을 넘어 서기 전에 실제로 어떻게 작동하는지 이해해야합니다.


11

상대적으로 간단히 C의 사고 방식은 "문제가 있습니까? 포인터를 사용하십시오"입니다. C 문자열, 함수 포인터, 반복 포인터, 포인터 포인터, void 포인터-멤버 포인터가있는 C ++ 초기에도 이것을 볼 수 있습니다.

그러나 C ++에서는 이러한 많은 작업 또는 모든 작업에 값을 사용할 수 있습니다. 함수 추상화가 필요하십니까? std::function. 함수의 가치입니다. std::string? 그것은 가치입니다. 그것은 문자열입니다. C ++에서 비슷한 접근 방식을 볼 수 있습니다. 이를 통해 인간과 컴파일러 모두가 코드를 훨씬 쉽게 분석 할 수 있습니다.


C ++에서 : 문제가 있습니까? 포인터를 사용하십시오. 이제 2 개의 문제가 있습니다.
Daniel Zazula

10

이유 중 하나는 너무 넓은 포인터 적용입니다. 그것들은 컨테이너를 반복하는 데 사용될 수 있으며 기능, 사소한 수명 관리, 메모리의 임의의 장소에 액세스 할 때 큰 객체 복사를 피할 수 있습니다. 한 가지 용도로 사용하면 다른 기능을 사용할 수 있습니다 의도적으로 즉시 독립적으로.

정확한 목적을위한 도구를 선택하면 반복을위한 반복자, 수명 관리를위한 스마트 포인터 등 코드가 더 단순하고 의도적으로 보이게됩니다.


3

이미 나열된 이유 외에도 더 나은 최적화가 있습니다. 앨리어싱 분석은 포인터 산술의 관점에서 너무 복잡하지만 참조는 최적화를 암시하므로 참조 만 사용하는 경우 훨씬 더 깊은 앨리어싱 분석이 가능합니다.


2

@jmquigley 포인터 및 포인터 산술에 의해 언급 된 메모리 누수의 위험 외에도 포인터는 메모리의 모든 위치를 가리켜 "버그를 찾기 어렵고"보안 상 취약성을 유발할 있기 때문에 문제가 될 수 있습니다 .

이것이 C #과 Java에서 거의 포기 된 이유입니다.


C #에서 "종료"되지 않았을 것으로 예상됩니다. 이 답변은 가난하고 철자가 끔찍하며 끔찍한 진술이 있습니다.
Ramhound

1
그들은 거의 버려졌습니다. 원어민이 아닌 것에 대해 사과드립니다.
k3b

1
우리는 철자를 도울 수 있습니다. 당신은 기대 또는 제외 말을 의미 했습니까?
DeveloperDon

1
C #에서 거의 포기 unsafe
되었지만

-1

C ++ 는 대부분의 C , 기능 및 객체 및 클래스를 지원합니다. C는 이미 포인터와 다른 것들을 가지고있었습니다.

포인터는 객체 방향과 결합 할 수있는 매우 유용한 기술이며 C ++에서 지원합니다. 그러나이 기술은 가르치기 어렵고 이해하기 어렵고 원치 않는 오류를 발생시키기가 매우 쉽습니다.

많은 새로운 프로그래밍 언어는 Java, .NET, Delphi, Vala, PHP, Scala와 같은 객체에 포인터를 사용하지 않는 척합니다. 그러나 포인터는 여전히 "장면 뒤에"사용됩니다. 이러한 "숨겨진 포인터"기술을 "참조"라고합니다.

어쨌든 포인터객체 지향 프로그래밍 뿐만 아니라 특정 문제를 해결하는 유효한 방법으로 프로그래밍 패턴으로 간주합니다 .

다른 개발자는 다른 의견을 가질 수 있습니다. 그러나 학생과 프로그래머는 다음을 수행하는 방법을 배우십시오.

(1) 객체없이 포인터를 사용하십시오.

(2) 포인터가없는 객체

(3) 객체에 대한 명시 적 포인터

(4) 객체에 대한 "숨겨진"포인터 (AKA 참조 ) ;-)

그와 같은 순서로.

가르치기가 어렵고 배우기가 어려운 경우에도 마찬가지입니다. 오브젝트 파스칼 (Delphi, FreePascal 등) 및 C++(Java 또는 C # 아님)을 이러한 목표에 사용할 수 있습니다.

나중에 초보자 프로그래머는 Java, C #, Object Oriented PHP 등의 프로그래밍 언어 인 "개체에 대한 숨겨진 포인터"로 이동할 수 있습니다.


19
C ++은 처음 시작한 "C with Classes"보다 훨씬 더 많습니다.
David Thornley

왜 C ++과 C를 공중 인용 부호로 묶고 있습니까? 그리고 "숨겨진", "참조"및 기타 모든 것? "판매원"이고 "프로그래밍"에 참여하지 않습니까?
phresnel

굵게 표시해야합니다. 따옴표를 사용하여 강조 할 수도 있지만 그 반대도
마찬가지입니다.

-6

VC6에 대해 이야기하면 클래스의 포인터 (인스턴스화)를 변수 (예 : DWORD)로 캐스팅 할 때이 포인터가 로컬 인 경우에도 동일한 힙을 사용하는 모든 함수를 통해 클래스에 액세스 할 수 있습니다. 인스턴스화 된 클래스는 로컬로 정의되지만 실제로는 그렇지 않습니다. 내가 아는 한 힙 변수, 구조 또는 클래스의 주소는 호스팅 클래스의 모든 수명 동안 고유합니다.

예:

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

편집 원래 코드의 일부입니다. CSRecodset 클래스는 모든 실제 코드가있는 CXdbRecordset의 캐스팅 클래스 일뿐입니다. 그렇게하면 사용자가 내 권리를 잃지 않고 내가 쓴 내용을 활용할 수 있습니다. 내 데이터베이스 엔진이 전문적이라는 것을 가장하지는 않지만 실제로 작동합니다.

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

편집 : DeadMG에 의해 요청 :

void nimportequoidumomentquecaroule(void) {

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

}

1
이 설명은 설명하는 내용을 설명하기 위해 일부 코드로 크게 향상 될 수 있습니다. 원래 질문과 관련이 있다는 생각이 들지만이 시나리오에서 위험에 대해 경고 한 경우 질문자의 주제를 자세히 설명하는 데 도움이됩니다.
DeveloperDon

1
캐스트 대상 DWORD은 모욕적이고 부정확 할 수 있습니다 (DWORD가 포인터를 보유 할만큼 넓을 필요는 없습니다). 타입이 지정되지 않은 포인터가 필요한 경우 void*C ++에서이를 필요로하는 경우 코드에 디자인 문제가있는 경우가 종종 있습니다.
Mat

살바도르, 당신이 VC6에 대해 말하려고 노력하고 있다고 생각합니다. 이 예제는 주석으로 도움이 될 수 있으며 컴파일러의 경고가 표시되면 질문에 대한 답변과 관련하여 유익한 정보가 될 수 있습니다.
DeveloperDon

@Mat이 예제는 32 비트 OS 및 VC6 ++ 컴파일러 용입니다.
살바도르

6
절대적으로 고대 컴파일러에서 잘못된 코드에 대해 이야기하고 있습니까? 고맙지 만 사양 할게.
DeadMG
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.