Có bao nhiêu và cách sử dụng của const const const trong C ++?


129

Là một lập trình viên C ++ mới làm quen, có một số cấu trúc trông vẫn rất mơ hồ đối với tôi, một trong số đó là const. Bạn có thể sử dụng nó ở rất nhiều nơi và với rất nhiều hiệu ứng khác nhau mà gần như không thể cho người mới bắt đầu sống sót. Một số guru C ++ sẽ giải thích một lần mãi mãi về các cách sử dụng khác nhau và liệu và / hoặc tại sao không sử dụng chúng?


chính xác tìm kiếm câu hỏi đó: D
alamin

Câu trả lời:


100

Đang cố gắng thu thập một số cách sử dụng:

Liên kết một số tạm thời để tham chiếu đến const, để kéo dài tuổi thọ của nó. Tham chiếu có thể là một cơ sở - và hàm hủy của nó không cần phải là ảo - hàm hủy phải vẫn được gọi là:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

Giải thích , sử dụng mã:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual
};

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { }
    ~Derived() {
        t(); // call function
    }
};

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }

Thủ thuật này được sử dụng trong lớp tiện ích ScopeGuard của Alexandrescu. Khi tạm thời vượt ra khỏi phạm vi, hàm hủy của Derogen được gọi chính xác. Đoạn mã trên bỏ lỡ một số chi tiết nhỏ, nhưng đó là vấn đề lớn với nó.


Sử dụng const để nói với các phương thức khác sẽ không thay đổi trạng thái logic của đối tượng này.

struct SmartPtr {
    int getCopies() const { return mCopiesMade; }
};

Sử dụng const cho các lớp copy-on-write , để làm cho trình biên dịch giúp bạn quyết định khi nào và khi nào bạn không cần sao chép.

struct MyString {
    char * getData() { /* copy: caller might write */ return mData; }
    char const* getData() const { return mData; }
};

Giải thích : Bạn có thể muốn chia sẻ dữ liệu khi bạn sao chép một cái gì đó miễn là dữ liệu của đối tượng ban đầu và copie'd vẫn giữ nguyên. Khi một trong các đối tượng thay đổi dữ liệu, tuy nhiên bạn cần có hai phiên bản: Một cho bản gốc và một cho bản sao. Đó là, bạn sao chép trên một ghi vào một trong hai đối tượng, để bây giờ cả hai đều có phiên bản riêng.

Sử dụng mã :

int main() {
    string const a = "1234";
    string const b = a;
    // outputs the same address for COW strings
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

Đoạn mã trên in cùng một địa chỉ trên GCC của tôi, vì thư viện C ++ đã sử dụng thực hiện sao chép khi ghi std::string. Cả hai chuỗi, mặc dù chúng là các đối tượng riêng biệt, chia sẻ cùng một bộ nhớ cho dữ liệu chuỗi của chúng. Tạo bnon-const sẽ thích phiên bản không const của operator[]GCC và GCC sẽ tạo một bản sao của bộ nhớ đệm sao lưu, vì chúng ta có thể thay đổi nó và nó không ảnh hưởng đến dữ liệu của a!

int main() {
    string const a = "1234";
    string b = a;
    // outputs different addresses!
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

Đối với trình xây dựng sao chép để tạo các bản sao từ các đối tượng const và tạm thời :

struct MyClass {
    MyClass(MyClass const& that) { /* make copy of that */ }
};

Để tạo các hằng số không thể thay đổi

double const PI = 3.1415;

Để chuyển các đối tượng tùy ý bằng tham chiếu thay vì theo giá trị - để ngăn việc truyền giá trị có thể tốn kém hoặc không thể

void PrintIt(Object const& obj) {
    // ...
}

2
Bạn có thể giải thích xin vui lòng sử dụng đầu tiên và thứ ba trong các ví dụ của bạn?
tunnuz

"Để đảm bảo callee rằng tham số không thể là NULL" Tôi không thấy const có liên quan gì đến ví dụ đó.
Logan Capaldo

Rất tiếc, tôi rất thất bại. tôi bằng cách nào đó bắt đầu viết về tài liệu tham khảo. cảm ơn bạn rất nhiều vì đã rên rỉ :) tất nhiên tôi sẽ loại bỏ những thứ đó ngay bây giờ :)
Johannes Schaub - litb

3
Hãy giải thích ví dụ đầu tiên. Không có ý nghĩa nhiều với tôi.
chikuba

28

Thực sự có 2 cách sử dụng const trong C ++.

Giá trị Const

Nếu một giá trị ở dạng biến, thành viên hoặc tham số sẽ không (hoặc không nên) bị thay đổi trong suốt vòng đời của nó, bạn nên đánh dấu nó là const. Điều này giúp ngăn ngừa đột biến trên đối tượng. Chẳng hạn, trong hàm sau, tôi không cần thay đổi thể hiện của Sinh viên để tôi đánh dấu nó const.

void PrintStudent(const Student& student) {
  cout << student.GetName();
}

Về lý do tại sao bạn sẽ làm điều này. Lý do về thuật toán sẽ dễ dàng hơn nhiều nếu bạn biết rằng dữ liệu cơ bản không thể thay đổi. "const" giúp, nhưng không đảm bảo điều này sẽ đạt được.

Rõ ràng, in dữ liệu lên cout không đòi hỏi nhiều suy nghĩ :)

Đánh dấu một phương thức thành viên là const

Trong ví dụ trước tôi đã đánh dấu Student là const. Nhưng làm thế nào mà C ++ biết rằng việc gọi phương thức GetName () trên sinh viên sẽ không làm biến đổi đối tượng? Câu trả lời là phương pháp được đánh dấu là const.

class Student {
  public:
    string GetName() const { ... }
};

Đánh dấu một phương thức "const" làm 2 việc. Chủ yếu nó nói với C ++ rằng phương thức này sẽ không làm biến đổi đối tượng của tôi. Điều thứ hai là tất cả các biến thành viên bây giờ sẽ được xử lý như thể chúng được đánh dấu là const. Điều này giúp nhưng không ngăn bạn sửa đổi thể hiện của lớp.

Đây là một ví dụ cực kỳ đơn giản nhưng hy vọng nó sẽ giúp trả lời câu hỏi của bạn.


16

Hãy cẩn thận để hiểu sự khác biệt giữa 4 tuyên bố này:

2 khai báo sau đây giống hệt nhau về mặt ngữ nghĩa. Bạn có thể thay đổi điểm ccp1 và ccp2, nhưng bạn không thể thay đổi thứ họ đang chỉ.

const char* ccp1;
char const* ccp2;

Tiếp theo, con trỏ là const, vì vậy để có ý nghĩa, nó phải được khởi tạo để trỏ đến một cái gì đó. Bạn không thể làm cho nó trỏ đến một cái gì đó khác, tuy nhiên điều mà nó chỉ ra có thể được thay đổi.

char* const cpc = &something_possibly_not_const;

Cuối cùng, chúng tôi kết hợp cả hai - vì vậy thứ được chỉ vào không thể được sửa đổi và con trỏ không thể trỏ đến bất kỳ nơi nào khác.

const char* const ccpc = &const_obj;

Quy tắc xoắn ốc theo chiều kim đồng hồ có thể giúp gỡ rối một khai báo http://c-faq.com/decl/spirus.anderson.html


Trong một đường vòng, có nó làm! Quy tắc xoắn ốc theo chiều kim đồng hồ mô tả nó tốt hơn - bắt đầu bằng tên (kpPulum) và vẽ một vòng xoắn theo chiều kim đồng hồ đi ra ngoài mã thông báo và nói từng mã thông báo. Rõ ràng, không có gì ở bên phải của kpPulum nhưng nó vẫn hoạt động.
Steve Folly

3

Một lưu ý nhỏ, khi tôi đọc ở đây , thật hữu ích khi nhận thấy rằng

const áp dụng cho bất cứ điều gì ở bên trái của nó (trừ khi không có gì trong trường hợp đó áp dụng cho bất cứ điều gì là quyền ngay lập tức của nó).

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.