Tại sao con trỏ không được khuyến nghị khi mã hóa bằng C ++?


45

Tôi đọc từ đâu đó rằng khi sử dụng C ++ thì không nên sử dụng con trỏ. Tại sao con trỏ lại là một ý tưởng tồi khi bạn đang sử dụng C ++. Đối với các lập trình viên C đã quen sử dụng các con trỏ, cách thay thế và cách tiếp cận tốt hơn trong C ++ là gì?


40
vui lòng liên kết đến "một nơi nào đó". Bối cảnh có thể rất phù hợp.

1
Câu hỏi này hy vọng hữu ích cho bạn.
Garet Claborn

Hầu hết các câu trả lời đề cập đến việc tránh rò rỉ bộ nhớ là lý do chính. Tôi không thể nhớ lần cuối cùng một trong những ứng dụng của chúng tôi gặp sự cố rò rỉ bộ nhớ mặc dù đã sử dụng con trỏ. Nếu bạn gặp vấn đề rò rỉ bộ nhớ thì bạn không sử dụng đúng công cụ hoặc bạn không biết mình đang làm gì. Hầu hết các môi trường phát triển đều có cách tự động kiểm tra rò rỉ được tích hợp. Tôi nghĩ rằng các vấn đề rò rỉ bộ nhớ khó theo dõi hơn trong các ngôn ngữ được thu gom rác vì sự xuất hiện của chúng tinh vi hơn rất nhiều và bạn thường cần một công cụ của bên thứ 3 để theo dõi thủ phạm .
Dunk

1
Thêm vào nhận xét của @Dunk, đôi khi các trình thu gom rác tích hợp trong các ngôn ngữ cấp cao hơn đơn giản là không hoạt động đúng. Chẳng hạn, trình thu gom rác của ActionScript 3 không có. Có một lỗi trong đó ngay bây giờ có liên quan đến các NetConnectiontrường hợp ngắt kết nối với máy chủ ( stackoverflow.com/questions/14780456/ trộm ), cũng như có một vấn đề với việc có nhiều đối tượng trong một chương trình mà nó sẽ từ chối thu thập ...
Panzercrisis

... ( adobe.com/devnet/ilitiescript/learning/as3-fundamentals/ triệt - tìm kiếm GCRoots are never garbage collected.và đoạn văn bắt đầu bởi The MMgc is considered a conservative collector for mark/sweep.). Về mặt kỹ thuật, đây là sự cố trong Adobe Virtual Machine 2, không phải bản thân AS3, nhưng khi bạn gặp vấn đề như thế này ở các ngôn ngữ cấp cao hơn, có bộ sưu tập rác về cơ bản, bạn thường không có cách nào để gỡ lỗi trong ngôn ngữ những vấn đề này hoàn toàn ra khỏi chương trình. ...
Panzercrisis

Câu trả lời:


58

Tôi nghĩ rằng họ có nghĩa là bạn nên sử dụng con trỏ thông minh thay vì con trỏ thông thường.

Trong khoa học máy tính, một con trỏ thông minh là một kiểu dữ liệu trừu tượng mô phỏng một con trỏ trong khi cung cấp các tính năng bổ sung, chẳng hạn như thu thập rác tự động hoặc kiểm tra giới hạn. Các tính năng bổ sung này nhằm giảm các lỗi gây ra bởi việc sử dụng sai con trỏ trong khi vẫn giữ được hiệu quả. Con trỏ thông minh thường theo dõi các đối tượng mà chúng trỏ đến nhằm mục đích quản lý bộ nhớ.

Việc sử dụng sai con trỏ là một nguồn chính của lỗi: việc phân bổ, phân bổ và tham chiếu liên tục phải được thực hiện bởi một chương trình được viết bằng cách sử dụng con trỏ dẫn đến nguy cơ rò rỉ bộ nhớ sẽ xảy ra. Con trỏ thông minh cố gắng ngăn chặn rò rỉ bộ nhớ bằng cách tự động phân bổ tài nguyên: khi con trỏ (hoặc cuối cùng trong một loạt các con trỏ) đến một đối tượng bị phá hủy, ví dụ vì nó nằm ngoài phạm vi, đối tượng nhọn cũng bị phá hủy.

Trong C ++, trọng tâm sẽ là thu gom rác và ngăn rò rỉ bộ nhớ (chỉ để đặt tên hai). Con trỏ là một phần cơ bản của ngôn ngữ, vì vậy không sử dụng chúng là không thể, ngoại trừ trong các chương trình trival nhất.


22
thông thường, bộ sưu tập rác không nghiêm ngặt theo nghĩa cổ điển, tính tham khảo nhiều hơn. Ít nhất là trong ptr thông minh mà tôi đã từng sử dụng ([boost | std] :: shared_ptr)
Doug T.

3
Câu trả lời này rất hạn chế đối với con trỏ thông minh, đây chỉ là một khía cạnh nhỏ của vấn đề. Ngoài ra, con trỏ thông minh không phải là bộ sưu tập rác.
deadalnix

3
Ngoài ra RAII nói chung.
Klaim

3
Không, đây không phải là ý nghĩa. Nó chỉ là một khía cạnh, và không phải là khía cạnh quan trọng nhất.
Konrad Rudolph

Tôi nghĩ rằng con trỏ thông minh là khía cạnh quan trọng nhất, nhưng tôi đồng ý có nhiều người khác.
DeadMG

97

Vì tôi là người đã xuất bản cuốn sách lịch sử không sử dụng con trỏ f * cking, tôi cảm thấy rằng tôi nên bình luận ở đây.

Trước hết, như một cuộc bút chiến, rõ ràng nó đại diện cho một quan điểm cực đoan. Có rất chắc chắn sử dụng hợp pháp của con trỏ (thô). Nhưng tôi (và nhiều lập trình viên C ++ chuyên nghiệp) cho rằng những trường hợp này cực kỳ hiếm. Nhưng những gì chúng tôi thực sự có nghĩa là sau đây:

Đầu tiên:

Con trỏ thô phải trong mọi trường hợp bộ nhớ riêng.

Ở đây, bộ nhớ của riêng về cơ bản, có nghĩa là tại một thời điểm nào đó deleteđược gọi trên con trỏ đó (nhưng nó chung chung hơn thế). Tuyên bố này có thể được thực hiện một cách an toàn như là một tuyệt đối. Các chỉ ngoại lệ là khi thực hiện con trỏ thông minh (hay chiến lược quản lý bộ nhớ khác) của riêng bạn. Và thậm chí ở đó bạn thường vẫn nên sử dụng một con trỏ thông minh ở mức thấp.

Lý do cho việc này khá đơn giản: con trỏ thô mà bộ nhớ riêng giới thiệu một nguồn lỗi. Và những lỗi này rất phổ biến trong phần mềm hiện có: rò rỉ bộ nhớ và xóa hai lần - cả hai đều là hậu quả trực tiếp của quyền sở hữu tài nguyên không rõ ràng (nhưng đi theo hướng ngược lại).

Vấn đề này có thể được loại bỏ hoàn toàn, hầu như không mất phí, chỉ bằng cách sử dụng con trỏ thông minh thay vì con trỏ thô (cảnh báo: điều này vẫn đòi hỏi phải suy nghĩ, tất nhiên, con trỏ chia sẻ có thể dẫn đến chu kỳ và do đó một lần nữa bị rò rỉ bộ nhớ - nhưng điều này dễ dàng tránh được).

Thứ hai:

Hầu hết việc sử dụng con trỏ trong C ++ là không cần thiết.

Không giống như các ngôn ngữ khác, C ++ hỗ trợ rất mạnh cho ngữ nghĩa giá trị và đơn giản là không cần đến sự chỉ dẫn của con trỏ. Điều này không được nhận ra ngay lập tức - trong lịch sử, C ++ được phát minh để tạo điều kiện cho việc định hướng đối tượng dễ dàng trong C và phụ thuộc rất nhiều vào việc xây dựng các biểu đồ đối tượng được kết nối bằng con trỏ. Nhưng trong C ++ hiện đại, mô hình này hiếm khi là lựa chọn tốt nhất và các thành ngữ C ++ hiện đại thường không cần đến con trỏ . Họ hoạt động trên các giá trị hơn là con trỏ.

Thật không may, thông báo này vẫn chưa được bắt gặp trong phần lớn cộng đồng người dùng C ++. Kết quả là, hầu hết các mã C ++ được viết vẫn bị lấp đầy bởi các con trỏ thừa, làm cho mã phức tạp, chậm và bị lỗi / không đáng tin cậy.

Đối với ai đó biết C ++ hiện đại, rõ ràng là bạn rất hiếm khi cần bất kỳ con trỏ nào (thông minh hoặc thô; ngoại trừ khi sử dụng chúng làm công cụ lặp). Mã kết quả là ngắn hơn, ít phức tạp hơn, dễ đọc hơn, thường hiệu quả hơn và đáng tin cậy hơn.


5
Thở dài ... đây thực sự nên là câu trả lời với hơn 30 lượt upvote ... đặc biệt là cho điểm thứ hai. Con trỏ chỉ hiếm khi cần thiết trong C ++ hiện đại.
Charles Salvia

1
những gì về ví dụ. Đối tượng Gui sở hữu một loạt các đối tượng doc. Nó có các con trỏ như cả hai để lớp có thể được khai báo chuyển tiếp (tránh biên dịch lại) và để đối tượng có thể được khởi tạo khi được tạo (với mới) thay vì được tạo trên ngăn xếp ở trạng thái trống nào đó và sau đó được đăng nhập? Điều này có vẻ như việc sử dụng con trỏ hoàn toàn hợp lệ - không phải tất cả các đối tượng được đóng gói đều nằm trong đống?
Martin Beckett

4
Các đối tượng GUI @Martin là một trường hợp trong đó đồ thị đối tượng con trỏ thực sự là giải pháp tốt nhất. Nhưng sắc lệnh chống lại con trỏ thô sở hữu bộ nhớ vẫn còn. Hoặc sử dụng các con trỏ được chia sẻ xuyên suốt hoặc phát triển một mô hình sở hữu phù hợp và chỉ có mối quan hệ yếu (= không sở hữu) giữa các điều khiển thông qua các con trỏ thô.
Konrad Rudolph

1
@ VF1 std::unique_ptr. Ngoài ra, tại sao không ptr_vec? Nhưng thường thì một vectơ giá trị vẫn sẽ trao đổi nhanh hơn (đặc biệt là với ngữ nghĩa di chuyển).
Konrad Rudolph

1
@gaazkam Không ai bắt bạn chỉ sử dụng các container tiêu chuẩn. Ví dụ, Boost có triển khai bản đồ với sự hỗ trợ cho các loại không hoàn chỉnh. Một giải pháp khác là sử dụng loại tẩy; boost::variantvới một recursive_wrappercó lẽ là giải pháp yêu thích của tôi để đại diện cho một DAG.
Konrad Rudolph

15

Đơn giản là vì có những khái niệm trừu tượng có sẵn cho bạn, điều này che giấu các khía cạnh bình thường hơn của việc sử dụng các con trỏ, chẳng hạn như truy cập vào bộ nhớ thô và dọn dẹp sau khi phân bổ của bạn. Với các con trỏ thông minh, các lớp container và các mẫu thiết kế như RAII, nhu cầu sử dụng các con trỏ thô bị giảm đi. Điều đó nói rằng, giống như bất kỳ sự trừu tượng nào, bạn nên hiểu cách chúng thực sự hoạt động trước khi vượt ra ngoài chúng.


11

Tương đối đơn giản, tâm lý C là "Có vấn đề? Sử dụng một con trỏ". Bạn có thể thấy điều này trong các chuỗi C, các con trỏ hàm, các con trỏ-as-iterators, con trỏ tới con trỏ, con trỏ void - ngay cả trong những ngày đầu của C ++ với các con trỏ thành viên.

Nhưng trong C ++, bạn có thể sử dụng các giá trị cho nhiều hoặc tất cả các tác vụ này. Cần một sự trừu tượng hóa chức năng? std::function. Đó là một giá trị đó là một chức năng. std::string? Đó là một giá trị, đó là một chuỗi. Bạn có thể thấy các cách tiếp cận tương tự trên C ++. Điều này làm cho việc phân tích mã dễ dàng hơn nhiều cho cả người và trình biên dịch.


Trong C ++: Có vấn đề? Sử dụng một con trỏ. Bây giờ bạn có 2 vấn đề ...
Daniel Zazula

10

Một trong những lý do là ứng dụng con trỏ quá rộng. Chúng có thể được sử dụng để lặp qua các container, để tránh sao chép các đối tượng lớn khi chuyển sang chức năng, quản lý thời gian sống không tầm thường, truy cập vào các vị trí ngẫu nhiên trong bộ nhớ, v.v. Và một khi bạn sử dụng chúng cho một mục đích, các tính năng khác của chúng sẽ khả dụng ngay lập tức độc lập về ý định.

Lựa chọn một công cụ cho mục đích chính xác làm cho mã đơn giản hơn và mục đích rõ ràng hơn - các trình lặp cho các lần lặp, các con trỏ thông minh để quản lý thời gian sống, v.v.


3

Bên cạnh những lý do đã được liệt kê, có một lý do rõ ràng: tối ưu hóa tốt hơn. Phân tích răng cưa quá phức tạp trong việc sử dụng một số liệu của con trỏ, trong khi các tham chiếu gợi ý một trình tối ưu hóa, do đó có thể phân tích răng cưa sâu hơn nhiều nếu chỉ sử dụng các tham chiếu.


2

Bên cạnh nguy cơ rò rỉ bộ nhớ được nêu bởi con số @jmquigley và số học con trỏ có thể được coi là có vấn đề vì con trỏ có thể chỉ ra mọi nơi trong bộ nhớ gây ra "lỗi khó tìm" và "lỗ hổng bảo mật".

Đó là lý do tại sao chúng gần như bị bỏ rơi trong C # và Java.


Hy vọng họ không bị "bỏ rơi" trong C #. Câu trả lời này là kém, có chính tả khủng khiếp, và một tuyên bố không chính xác khủng khiếp.
Ramhound

1
Họ gần như bị bỏ rơi. Tôi xin lỗi vì đã không nuôi ong một người bản ngữ.
k3b

1
Này, chúng ta có thể giúp với chính tả. Ý của bạn là nói mong đợi hay ngoại trừ?
DeveloperDon

1
Gần như bị bỏ rơi trong C #, bạn vẫn có thể bật con trỏ bằng cách chỉ định unsafetừ khóa
linquize

-1

C ++ hỗ trợ hầu hết C , tính năng, cộng với Đối tượng và Lớp. C đã có con trỏ và những thứ khác.

Con trỏ là một kỹ thuật rất hữu ích, có thể kết hợp với Định hướng đối tượng và C ++ hỗ trợ chúng. Nhưng, kỹ thuật này, khó dạy và khó hiểu, và, rất dễ gây ra lỗi không mong muốn.

Nhiều ngôn ngữ lập trình mới giả vờ không sử dụng con trỏ với các đối tượng, như Java, .NET, Delphi, Vala, PHP, Scala. Nhưng, con trỏ vẫn được sử dụng, "đằng sau hậu trường". Các kỹ thuật "con trỏ ẩn" này được gọi là "tài liệu tham khảo".

Dù sao, tôi coi con trỏ là một mẫu lập trình, như một cách hợp lệ để giải quyết các vấn đề nhất định, cũng như lập trình hướng đối tượng .

Các nhà phát triển khác có thể có một ý kiến ​​khác. Nhưng, tôi đề nghị sinh viên và lập trình viên học cách:

(1) Sử dụng con trỏ không có đối tượng

(2) các đối tượng không có con trỏ

(3) con trỏ rõ ràng đến các đối tượng

(4) con trỏ "ẩn" đến các đối tượng ( tham chiếu AKA ) ;-)

Theo thứ tự đó.

Ngay cả khi khó dạy, và khó học. Đối tượng Pascal (Delphi, FreePascal, những người khác) và C++(không phải Java hoặc C #) có thể được sử dụng cho các mục tiêu đó.

Và, sau này, các lập trình viên mới làm quen, có thể chuyển sang "các con trỏ ẩn đến các đối tượng" các ngôn ngữ lập trình như: Java, C #, PHP hướng đối tượng và các ngôn ngữ khác.


19
C ++ là rất nhiều so với "C với các lớp" mà nó bắt đầu như là.
David Thornley

Tại sao bạn gói C ++ và C vào dấu ngoặc kép? Và "ẩn", "tài liệu tham khảo" và mọi thứ khác? Bạn có phải là "nhân viên bán hàng" và không tham gia "lập trình" không?
phresnel

Tôi nên đặt chúng in đậm. Trích dẫn có thể được sử dụng để làm nổi bật nhưng cũng ngược lại
umlcat

-6

Nói về VC6, khi bạn chuyển một con trỏ của một lớp (mà bạn khởi tạo) thành một biến (ví dụ DWORD), ngay cả khi con trỏ này là cục bộ, bạn có thể truy cập lớp trên tất cả các hàm sử dụng cùng một heap. Lớp khởi tạo được định nghĩa là cục bộ nhưng thực tế thì không phải vậy. Theo như tôi biết, bất kỳ địa chỉ nào của một biến heap, cấu trúc hoặc lớp là duy nhất trong suốt vòng đời của lớp lưu trữ.

Thí dụ:

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 Đó là một phần rất nhỏ của mã gốc. Lớp CSRecodset chỉ là một lớp đúc của CXdbRecordset, trong đó tất cả các mã thực sự là. Làm như vậy tôi có thể cho phép người dùng hưởng lợi từ những gì tôi đã viết mà không mất quyền. Tôi không giả vờ chứng minh rằng công cụ cơ sở dữ liệu của tôi là chuyên nghiệp nhưng nó thực sự hoạt động.

//-------------------------------------
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: được yêu cầu bởi DeadMG:

void nimportequoidumomentquecaroule(void) {

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

}

1
Mô tả này có thể được tăng cường đáng kể bởi một số mã để minh họa những gì bạn đang mô tả. Tôi có cảm giác rằng nó liên quan đến câu hỏi ban đầu, nhưng nếu bạn cảnh báo chúng tôi về sự nguy hiểm trong kịch bản này, nó sẽ giúp xây dựng chủ đề của người hỏi.
Nhà phát

1
Điều đó DWORDbị lạm dụng và có thể không chính xác (DWORD không nhất thiết đủ rộng để giữ một con trỏ). Nếu bạn cần một con trỏ chưa được gõ, hãy sử dụng void*- nhưng khi bạn thấy mình cần điều đó trong C ++, bạn thường gặp vấn đề về thiết kế trong mã của mình, bạn nên sửa.
Mat

Salvador, tôi nghĩ rằng bạn đang cố gắng nói điều gì đó về VC6 và cách nó xử lý con trỏ bất thường và bất ngờ. Ví dụ có thể có lợi bằng các bình luận và nếu bạn thấy các cảnh báo từ trình biên dịch, chúng có thể có nhiều thông tin liên quan đến câu trả lời của bạn cho câu hỏi.
Nhà phát triển vào

@Mat Ví dụ đó dành cho HĐH 32 bit và trình biên dịch VC6 ++.
Salvador

6
Nói về mã xấu trong một trình biên dịch hoàn toàn cổ xưa? Không, cám ơn.
DeadMG
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.