Tại sao một hàm thành viên const có thể sửa đổi một thành viên dữ liệu tĩnh?


86

Trong C++chương trình sau , việc sửa đổi thành viên dữ liệu tĩnh từ một consthàm đang hoạt động tốt:

class A 
{
  public:   
    static int a; // static data member

    void set() const
    {
        a = 10;
    }
};

Nhưng việc sửa đổi thành viên dữ liệu không tĩnh từ một consthàm không hoạt động:

class A 
{
  public:   
    int a; // non-static data member

    void set() const
    {
        a = 10;
    }
};

Tại sao một consthàm thành viên có thể sửa đổi một staticthành viên dữ liệu?


Sẽ rất hữu ích nếu bạn có thể cho chúng tôi biết bạn đang làm việc với nền tảng và trình biên dịch nào? Vì vậy, chúng tôi có thể xác định xem hành vi đó có phải là lỗi liên quan đến thiết lập cụ thể của bạn hay hành vi đó thực sự đúng và chỉ cần được giải thích.
Alex Zywicki

Trình biên dịch @AlexZywicki G ++ trên nền tảng Linux.
msc

8
Không cần. Đó là chủ ý và tất cả các trình biên dịch C ++ phải hỗ trợ nó. Nhưng tại sao những câu hỏi hay như thế này lại không được ủng hộ nữa?
Bathsheba

18
Đó là một bản dupe, nhưng nó được viết tốt hơn cái khác nhờ MCVE tốt hơn, vì vậy tôi đã sử dụng nó làm mục tiêu.
Baum mit Augen

5
Động lực ở đây constcó nghĩa là một chức năng thành viên của một đối tượng không thể sửa đổi một đối tượng đó . Nó có thể sửa đổi các đối tượng khác của cùng một lớp hoặc staticdữ liệu, được liên kết với lớp, không phải bất kỳ trường hợp cụ thể nào của nó. (Hoặc mutablecác thành viên dữ liệu, được tạo ra để trở thành ngoại lệ cho quy tắc này.)
Davislor

Câu trả lời:


100

Đó là quy tắc, vậy thôi. Và vì lý do chính đáng.

Định nghĩa consttrên một hàm thành viên có nghĩa là bạn không thể sửa đổi các biến thành viên không mutablephải là staticthành viên.

Bằng cách cung cấp một số hợp lý hóa, thiscon trỏ trong một consthàm thành viên đủ điều kiện là một constkiểu và thisvốn có liên quan đến một thể hiện của một lớp. staticcác thành viên không liên quan đến một cá thể lớp. Bạn không cần một phiên bản để sửa đổi một staticthành viên: bạn có thể làm điều đó, trong trường hợp của bạn, bằng cách viết A::a = 10;.

Vì vậy, trong trường hợp đầu tiên của bạn, hãy nghĩ a = 10;là viết tắt cho A::a = 10;và trong trường hợp thứ hai, hãy nghĩ nó là viết tắt cho this->a = 10;, không thể tổng hợp được vì loại thisconst A*.


1
Chỉ là một lỗi nhỏ ở đây: Vì bạn không thể gán lại thiscon trỏ, nó sẽ thuộc loại const A* const trong consttrường hợp của.
Taylor Hansen

2
@TaylorHansen thislà một prvalue của loại con trỏ. Giá trị của các loại không phải hạng không bao giờ đủ tiêu chuẩn cv.

21

Theo Tiêu chuẩn C ++ (9.2.3.2 Các thành viên dữ liệu tĩnh)

1 Thành viên dữ liệu tĩnh không phảimột phần của subobjects của một lớp ...

Và (9.2.2.1 Con trỏ this)

1 Trong phần thân của một hàm thành viên không tĩnh (9.2.1), từ khóa đây là một biểu thức prvalue có giá trị là địa chỉ của đối tượng mà hàm được gọi. Loại này trong một hàm thành viên của lớp X là X *. Nếu hàm thành viên được khai báo là const, thì kiểu này là const X * , ...

Và cuối cùng (9.2.2 Các hàm thành viên không tĩnh)

3 ... nếu tra cứu tên (3.4) giải quyết tên trong biểu thức id thành một thành viên không phải kiểu tĩnh của một số lớp C và nếu biểu thức id có khả năng được đánh giá hoặc C là X hoặc một lớp cơ sở của X, biểu thức id được chuyển thành biểu thức truy cập thành viên lớp (5.2.5) sử dụng (* this) (9.2.2.1) làm biểu thức hậu tố ở bên trái của. nhà điều hành.

Do đó trong định nghĩa lớp này

class A 
{
  public:   
    static int a; 

    void set() const
    {
        a = 10;
    }
};

thành viên dữ liệu tĩnh akhông phải là đối tượng subobject của đối tượng thuộc kiểu lớp và con trỏ thiskhông được sử dụng để truy cập thành viên dữ liệu tĩnh. Vì vậy, bất kỳ hàm thành viên nào, hằng số không tĩnh hoặc không hằng số, hoặc một hàm thành viên tĩnh đều có thể thay đổi thành viên dữ liệu vì nó không phải là hằng số.

Trong định nghĩa lớp này

class A 
{
  public:   
    int a; 

    void set() const
    {
        a = 10;
    }
};

thành viên dữ liệu không tĩnh alà một subobject của một đối tượng thuộc loại lớp. Để truy cập nó trong một chức năng thành viên, người ta sử dụng cú pháp truy cập thành viên của cú pháp này. Bạn không thể sử dụng một con trỏ hằng thisđể sửa đổi thành viên dữ liệu. Và con trỏ này thực sự có kiểu const A *bên trong hàm setvì hàm được khai báo với định nghĩa const. Nếu hàm không có định nghĩa trong trường hợp này, thành viên dữ liệu có thể bị thay đổi.


13

Vấn đề là, nếu một hàm thành viên của một lớp Aconst, thì kiểu của thisconst X*, và do đó ngăn các thành viên dữ liệu không tĩnh bị thay đổi (ví dụ: tiêu chuẩn C ++ ):

9.3.2 Con trỏ this [class.this]

Trong phần thân của một hàm thành viên không tĩnh (9.3), từ khóa đây là một biểu thức prvalue có giá trị là địa chỉ của đối tượng mà hàm được gọi. Loại này trong một hàm thành viên của lớp X là X *. Nếu hàm thành viên được khai báo là const, thì kiểu này là const X *, ...

Nếu alà thành viên dữ liệu không tĩnh, thì a=10giống với this->a = 10, không được phép nếu kiểu thisconst A*achưa được khai báo là mutable. Như vậy, kể từ khi void set() constlàm cho các loại thishạnh phúcconst A* , quyền truy cập này không được phép.

aNgược lại, nếu là một thành viên dữ liệu tĩnh, thì a=10không liên quan thisgì cả; và miễn là static int abản thân nó chưa được khai báo là const, câu lệnh a=10được cho phép.


1

Bộ định lượng consttrên một hàm thành viên có nghĩa là bạn không thể sửa đổi non-mutable, non-static các thành viên dữ liệu lớp .

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.