Khi nào tôi sử dụng dấu chấm, mũi tên hoặc dấu hai chấm để chỉ các thành viên của một lớp trong C ++?


243

Đến từ ngôn ngữ C có nguồn gốc từ khác (như Java hay C #) để C ++, đó là lúc đầu rất khó hiểu mà C ++ có ba cách để tham khảo cho các thành viên của một lớp: a::b, a.b, và a->b. Khi nào tôi sử dụng một trong những toán tử này?

(Lưu ý: Đây có nghĩa là một mục trong Câu hỏi thường gặp về C ++ của Stack Overflow . Nếu bạn muốn phê bình ý tưởng cung cấp Câu hỏi thường gặp trong biểu mẫu này, thì bài đăng trên meta bắt đầu tất cả điều này sẽ là nơi để thực hiện điều đó. câu hỏi đó được theo dõi trong phòng chat C ++ , nơi ý tưởng FAQ bắt đầu ngay từ đầu, vì vậy câu trả lời của bạn rất có thể được đọc bởi những người nghĩ ra ý tưởng.)

Câu trả lời:


248

Ba toán tử riêng biệt C ++ sử dụng để truy cập các thành viên của một đối tượng lớp hoặc lớp, cụ thể là dấu hai chấm ::, dấu chấm .và mũi tên ->, được sử dụng cho ba kịch bản khác nhau luôn được xác định rõ. Biết được điều này cho phép bạn ngay lập tức biết khá nhiều về abchỉ cần nhìn vào a::b, a.bhoặc a->b, tương ứng, trong bất kỳ mã bạn nhìn vào.

  1. a::bchỉ được sử dụng nếu blà thành viên của lớp (hoặc không gian tên) a. Đó là, trong trường hợp anày sẽ luôn là tên của một lớp (hoặc không gian tên).

  2. a.bchỉ được sử dụng nếu blà thành viên của đối tượng (hoặc tham chiếu đến đối tượng) a. Vì vậy a.b, asẽ luôn là một đối tượng thực tế (hoặc một tham chiếu đến một đối tượng) của một lớp.

  3. a->blà, ban đầu, một ký hiệu viết tắt cho (*a).b. Tuy nhiên, ->là chỉ các toán tử truy cập thành viên có thể bị quá tải, vì vậy nếu alà một đối tượng của một lớp bị quá tải operator->(các loại phổ biến như vậy là các con trỏ thông minh và các trình lặp), thì ý nghĩa là bất cứ điều gì mà nhà thiết kế lớp thực hiện. Để kết luận: Với a->b, nếu alà một con trỏ, bsẽ là thành viên của đối tượng mà con trỏ ađề cập đến. Tuy nhiên, nếu amột đối tượng của một lớp làm quá tải toán tử này, thì hàm toán tử bị quá tải operator->()sẽ được gọi.


Bản in nhỏ:

  • Trong C ++, các loại khai báo là class, structhoặc unionđược coi là "của kiểu lớp". Vì vậy, ở trên đề cập đến cả ba trong số họ.
  • Các tham chiếu về mặt ngữ nghĩa là bí danh cho các đối tượng, vì vậy tôi cũng nên thêm "hoặc tham chiếu đến một con trỏ" vào số 3. Tuy nhiên, tôi nghĩ rằng điều này sẽ gây nhầm lẫn hơn là hữu ích, vì các tham chiếu đến con trỏ ( T*&) hiếm khi được sử dụng.
  • Các toán tử dấu chấm và mũi tên có thể được sử dụng để chỉ các thành viên lớp tĩnh từ một đối tượng, mặc dù chúng không phải là thành viên của đối tượng. (Cảm ơn Oli vì đã chỉ ra điều này!)

10
Có thể cần phải làm rõ điều đó .->cũng có thể được sử dụng để truy cập các thống kê lớp thông qua một đối tượng, mặc dù chúng không hoàn toàn là "thành viên của đối tượng".
Oliver Charlesworth

@Oli: Điều đó thực sự đúng. Tôi đã thêm nó vào bản in nhỏ, vì tôi nghĩ nó không phổ biến và đủ quan trọng để được liệt kê trong văn bản chính.
sbi

3
Để hoàn thiện, có thể đáng để chỉ ra rằng operator*()cũng có thể bị quá tải, và không có gì buộc quá tải phải phù hợp operator->()! (Tôi đã không đánh giá thấp BTW, chỉ cần đến đây thông qua một chuỗi dài các bản sao)
juanchopanza

@OliCharlesworth bạn có biết nơi nào được chỉ định trong tiêu chuẩn C ++ không?
con lợn

1
@juanchopanza: Tuy nhiên, bạn không thể có hành vi xâu chuỗi ->bằng cách quá tải operator*và sử dụng .. Chỉ có operator->quá tải mới có được điều đó.
Ben Voigt

36

Đề xuất một giải pháp thay thế cho điểm 3 của sbi

a->bchỉ được sử dụng nếu alà một con trỏ. Nó là một tốc ký cho (*a).b, bthành viên của đối tượng atrỏ đến. C ++ có hai loại con trỏ là "con trỏ thông thường" và con trỏ thông minh. Đối với các con trỏ thông thường như A* a, trình biên dịch thực hiện ->. Đối với con trỏ thông minh như std::shared_ptr<A> a, ->là một chức năng thành viên của lớp shared_ptr.

Đặt vấn đề: đối tượng mục tiêu của Câu hỏi thường gặp này không viết con trỏ thông minh. Họ không cần phải biết ->thực sự được gọi operator->(), hoặc đó là phương thức truy cập thành viên duy nhất có thể bị quá tải.


4
Cho dù tôi có đồng ý hay không, tôi +1chỉ đưa ra một câu trả lời khác.
sbi

2
Chà, công bằng mà nói ->cũng là quá tải đối với các trình vòng lặp tiêu chuẩn mà bất kỳ lập trình viên C ++ nào cũng sẽ sớm gặp, vì vậy việc nói rằng nó chỉ được sử dụng cho các con trỏ có thể gây nhầm lẫn.
Kiscsirke

@Kiscsirke "lập trình viên C ++ thông thường" không cần phải viết các kiểu con trỏ hoặc trình lặp thông minh, chỉ cần sử dụng chúng. "Các cuộc hội thảo như một con trỏ" áp dụng cho cả hai.
Caleth

0
#include <iostream>
#include <string>

using namespace std;

class Human {
private:
    int age;

public:
    string name;

    Human(int humanAge, string humanName) 
         : age(humanAge), name(std::move(humanName)) {}

    void DoSomething() {
        cout << age << endl;
    }

    static void DisplayAge(const Human& person) {
        cout << person.age << endl;
    }

    // ...
};

int main() {
    // Usage of Dot(.) 
    Human firstMan(13, "Jim"); // firstMan is an instance of class Human
    cout << firstMan.name << endl; // accessing member attributes
    firstMan.DoSomething(); // accessing member functions

    // Usage of Pointer Operator (->)
    Human* secondMan = new Human(24, "Tom");
    cout << secondMan->name << endl; // accessing member attributes
    secondMan->DoSomething(); // accessing member functions
    cout << (*secondMan).name << endl; // accessing member attributes
    (*secondMan).DoSomething(); // accessing member functions

    // Usage of Double Colon (::)
    Human::DisplayAge(firstMan);
    firstMan.DisplayAge(firstMan); // ok but not recommended
    secondMan->DisplayAge(firstMan); // ok but not recommended

    delete(secondMan);

    return 0;
}

Từ ví dụ mã hóa ở trên, chúng ta thấy rằng:
* Truy cập các thành viên (thuộc tính và hàm) từ một thể hiện (hoặc đối tượng) bằng cách sử dụng toán tử dấu chấm ( .)
* Truy cập các thành viên (thuộc tính và hàm) từ một con trỏ đến một đối tượng (hoặc được tạo bởi new) bằng cách sử dụng toán tử con trỏ ( ->)
* Truy cập các hàm thành viên tĩnh từ chính lớp mà không có đối tượng làm tay cầm bằng cách sử dụng dấu hai chấm ( ::). [ Lưu ý: bạn cũng có thể gọi hàm thành viên tĩnh từ một thể hiện có .hoặc ->không được khuyến nghị]


@sbi gắt gỏng ha, tôi biết đó là một kiểu lặp đi lặp lại. Tôi chỉ muốn đưa ra một ví dụ rõ ràng để chỉ ra cách sử dụng chúng. Và nơi tôi đã nói ->chỉ có thể được sử dụng bởi một con trỏ được phân bổ trên heap bởi new? Dưới đây, mục thứ hai, tôi nghĩ rằng tôi thực sự làm rõ nó ->là cho con trỏ. Và trước khi bạn downvote, bạn nên thử className::non_static_member_function()với c ++ 14 một mình. Tham chiếu không phải là một con trỏ, vì vậy nó có thể sử dụng ., và tôi sẽ làm cho nó rõ ràng hơn trong câu trả lời của tôi.
Hu Xixi

0

Toán tử chấm được sử dụng trong các kịch bản lựa chọn thành viên trực tiếp.

print(a.b)

Ở đây, chúng tôi đang truy cập b, là thành viên trực tiếp của một đối tượng a. Vì vậy, chủ yếu, alà một đối tượng và blà thành viên (hàm / biến, v.v.) của a.


Toán tử mũi tên được sử dụng trong các kịch bản lựa chọn thành viên gián tiếp.

print(a->b)

Ở đây, chúng tôi đang truy cập bmà là một thành viên của đối tượng, được chỉ ra bởi a. Nó là viết tắt của (*a).bvà ở đây, achủ yếu là một con trỏ đến một đối tượng và blà một thành viên của đối tượng đó.


Toán tử Double Colon (Phạm vi) được sử dụng trong các kịch bản lựa chọn thành viên trực tiếp liên quan đến không gian tên.

print(a::b)

Ở đây, chúng tôi đang truy cập blà thành viên của lớp / không gian tên a. Chủ yếu, alà một lớp / không gian tên và blà thành viên (hàm / biến, v.v.) của a.

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.