2011-10-26 22 views
32

Trong khi tôi điều tra mã nguồn của Qt tôi thấy rằng trolltech guys sử dụng một cách rõ ràng this từ khóa để truy cập vào một trường trên destructor.Trong một lớp dẫn xuất có khuôn mẫu, tại sao tôi cần phải đủ điều kiện cho các tên thành viên lớp cơ sở với "this->" bên trong một hàm thành viên?

inline ~QScopedPointer() 
{ 
    T *oldD = this->d; 
    Cleanup::cleanup(oldD); 
    this->d = 0; 
} 

Vì vậy, điểm của việc sử dụng này là gì? Có lợi ích gì không?

Edit: Đối với những người bỏ phiếu cho đóng câu hỏi này, tôi nghi ngờ rằng việc sử dụng này là dành cho một số trường hợp lớp thừa kế

Một phần của QScopedPointer class định nghĩa:

template <typename T, typename Cleanup = QScopedPointerDeleter<T> > 
class QScopedPointer 

Trả lời

39

C++ câu trả lời (câu trả lời chung)

Hãy xem xét một lớp mẫu Derived với một lớp mẫu cơ sở:

template <typename T> 
class Base { 
public: 
    int d; 
}; 

template <typename T> 
class Derived : public Base<T> { 
    void f() { 
     this->d = 0; 
    } 
}; 

this có loại Derived<T>, loại phụ thuộc vào T. Vì vậy, this có một loại phụ thuộc. Vì vậy, this->d làm cho d một tên phụ thuộc. Các tên phụ thuộc được tra cứu trong ngữ cảnh của định nghĩa mẫu như các tên không phụ thuộc và trong bối cảnh instantiation.

Nếu không có this->, tên d sẽ chỉ được tra cứu dưới dạng tên không phụ thuộc và không được tìm thấy.

Một giải pháp khác là khai báo d trong mẫu định nghĩa riêng của mình:

template <typename T> 
class Derived : public Base<T> { 
    using Base::d; 
    void f() { 
     d = 0; 
    } 
}; 

Qanswer (câu trả lời cụ thể)

d là một member of QScopedPointer. Nó không phải là một thành viên được thừa kế. this-> không cần thiết ở đây.

OTOH, QScopedArrayPointer is a template class and d is an inherited member of a template base class:

template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> > 
class QScopedArrayPointer : public QScopedPointer<T, Cleanup> 

nên this-> là cần thiết here:

inline T &operator[](int i) 
{ 
    return this->d[i]; 
} 

Thật dễ dàng để thấy rằng nó dễ dàng hơn để chỉ cần đặt this-> ở khắp mọi nơi.

Hiểu được lý do

Tôi đoán nó không phải là rõ ràng đối với tất cả người dùng C++ tại sao tên được nhìn-up trong các lớp cơ sở không phụ thuộc nhưng không phải trong lớp cơ sở phụ thuộc:

class Base0 { 
public: 
    int nd; 
}; 

template <typename T> 
class Derived2 : 
     public Base0, // non-dependent base 
     public Base<T> { // dependent base 
    void f() { 
     nd; // Base0::b 
     d; // lookup of "d" finds nothing 

     f (this); // lookup of "f" finds nothing 
        // will find "f" later 
    } 
}; 

Có một lý do bên cạnh "tiêu chuẩn nói như vậy": nguyên nhân của cách gắn kết tên trong các mẫu hoạt động.

Mẫu có thể có tên bị ràng buộc muộn, khi mẫu được khởi tạo: ví dụ f trong f (this). Tại điểm Derived2::f() định nghĩa, không có biến, chức năng hoặc tên loại f được biết bởi trình biên dịch.Tập hợp các thực thể đã biết mà f có thể tham chiếu là rỗng vào thời điểm này. Đây không phải là vấn đề bởi vì trình biên dịch biết rằng nó sẽ tra cứu f sau này dưới dạng tên hàm hoặc tên hàm mẫu.

OTOH, trình biên dịch không biết phải làm gì với d; nó không phải là tên hàm (gọi). Không có cách nào để thực hiện việc ràng buộc trễ các tên hàm không được gọi.

Bây giờ, tất cả điều này có vẻ giống như kiến ​​thức cơ bản về đa hình mẫu thời gian biên dịch. Câu hỏi thực sự có vẻ là: tại sao không phải là d ràng buộc với Base<T>::d ở thời gian định nghĩa mẫu?

Vấn đề thực sự là không có Base<T>::d tại thời điểm định nghĩa mẫu, vì không có loại hoàn chỉnh Base<T> tại thời điểm đó: Base<T> được khai báo nhưng không được xác định! Bạn có thể hỏi: về điều này:

template <typename T> 
class Base { 
public: 
    int d; 
}; 

có vẻ như định nghĩa của một loại hoàn chỉnh!

Trên thực tế, cho đến khi instantiation, có vẻ như hơn:

template <typename T> 
class Base; 

để trình biên dịch. Không thể tra cứu tên trong mẫu lớp học! Nhưng chỉ trong một chuyên môn mẫu (instantiation). Mẫu là một nhà máy để tạo chuyên môn mẫu, mẫu không phải là một bộ chuyên môn mẫu. Trình biên dịch có thể tra cứu d trong Base<T> đối với bất kỳ loại cụ thể nào T, nhưng không thể tra cứu d trong mẫu lớp Base. Cho đến khi một loại T được xác định, Base<T>::d vẫn là trừu tượng Base<T>::d; chỉ khi loại T được biết, Base<T>::d bắt đầu đề cập đến một biến loại int.

Hậu quả của việc này là các lớp mẫuDerived2 có một nền tảng hoàn toàn lớp Base0 nhưng một không đầy đủ (về phía trước tuyên bố) lớp cơ sở Base. Chỉ cho một loại đã biết T, "lớp mẫu" (chuyên môn của mẫu lớp) Derived2<T> có một lớp cơ sở hoàn chỉnh, giống như bất kỳ lớp bình thường nào.

Bây giờ bạn thấy rằng:

template <typename T> 
class Derived : public Base<T> 

thực sự là một cơ sở đặc điểm kỹ thuật lớp mẫu (một nhà máy để làm cho thông số kỹ thuật lớp cơ sở) mà sau quy tắc khác nhau từ một đặc tả lớp cơ sở trong một bản mẫu.

Ghi chú: Người đọc có thể nhận thấy rằng tôi đã tạo thành một vài cụm từ ở cuối giải thích.

này là rất khác nhau: ở đây d là một cái tên có trình độ trong Derived<T>, và Derived<T> phụ thuộc kể từ T là một tham số mẫu. Một tên đủ điều kiện có thể bị trễ hạn ngay cả khi nó không phải là tên hàm (gọi).

Tuy nhiên, một giải pháp khác là:

template <typename T> 
class Derived : public Base<T> { 
    void f() { 
     Derived::d = 0; // qualified name 
    } 
}; 

Đây là tương đương.

Nếu bạn nghĩ rằng bên trong định nghĩa của Derived<T>, việc xử lý Derived<T> là một lớp hoàn toàn được biết đến đôi khi và là một lớp không xác định một số lần khác trong không nhất quán, tốt, bạn là đúng.

+0

** Lưu ý của người kiểm duyệt ** Các tính năng trong câu trả lời này đã bị xóa vì chúng bị biến thành tiếng ồn thuần túy. Vui lòng sử dụng [trò chuyện] để thảo luận mở rộng và nhớ vẫn còn dân sự._ –

1

Tôi đoán nó liên quan đến việc sử dụng quá tải của quy trình Dọn dẹp(). Kiểu đang được truyền được điều khiển một cách rõ ràng bởi kiểu mẫu T, do đó có thể kiểm soát phiên bản quá tải của Cleanup() được gọi.

Các vấn đề liên quan