Ý nghĩa của 'const' cuối cùng trong một khai báo hàm của một lớp?


727

Ý nghĩa của các consttuyên bố như thế này là gì? Những điều constlàm tôi bối rối.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

Câu trả lời:


951

Khi bạn thêm consttừ khóa vào một phương thức, thiscon trỏ về cơ bản sẽ trở thành một con trỏ tới constđối tượng và do đó bạn không thể thay đổi bất kỳ dữ liệu thành viên nào. (Trừ khi bạn sử dụng mutable, nhiều hơn về điều đó sau).

Các consttừ khóa là một phần của chữ ký chức năng có nghĩa là bạn có thể thực hiện hai phương pháp tương tự, một trong đó được gọi khi đối tượng là const, và một trong đó không phải là.

#include <iostream>

class MyClass
{
private:
    int counter;
public:
    void Foo()
    { 
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        std::cout << "Foo const" << std::endl;
    }

};

int main()
{
    MyClass cc;
    const MyClass& ccc = cc;
    cc.Foo();
    ccc.Foo();
}

Điều này sẽ xuất

Foo
Foo const

Trong phương thức non-const, bạn có thể thay đổi các thành viên thể hiện, điều mà bạn không thể làm trong constphiên bản. Nếu bạn thay đổi khai báo phương thức trong ví dụ trên thành mã bên dưới, bạn sẽ gặp một số lỗi.

    void Foo()
    {
        counter++; //this works
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++; //this will not compile
        std::cout << "Foo const" << std::endl;
    }

Điều này không hoàn toàn đúng, bởi vì bạn có thể đánh dấu một thành viên là mutablevà một constphương thức có thể thay đổi nó. Nó chủ yếu được sử dụng cho các quầy nội bộ và công cụ. Giải pháp cho điều đó sẽ là đoạn mã dưới đây.

#include <iostream>

class MyClass
{
private:
    mutable int counter;
public:

    MyClass() : counter(0) {}

    void Foo()
    {
        counter++;
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++;    // This works because counter is `mutable`
        std::cout << "Foo const" << std::endl;
    }

    int GetInvocations() const
    {
        return counter;
    }
};

int main(void)
{
    MyClass cc;
    const MyClass& ccc = cc;
    cc.Foo();
    ccc.Foo();
    std::cout << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
}

cái nào sẽ xuất

Foo
Foo const
Foo has been invoked 2 times

187

Các const có nghĩa là phương thức hứa hẹn sẽ không thay đổi bất kỳ thành viên nào trong lớp. Bạn có thể thực thi các thành viên của đối tượng được đánh dấu, ngay cả khi chính đối tượng được đánh dấu const:

const foobar fb;
fb.foo();

sẽ là hợp pháp.

Xem có bao nhiêu và cách sử dụng của con const const trong C ++? để biết thêm thông tin.


47

Vòng constloại có nghĩa là các phương thức có thể được gọi trên bất kỳ giá trị nào của foobar. Sự khác biệt xuất hiện khi bạn xem xét việc gọi một phương thức không const trên một đối tượng const. Xem xét nếu foobarloại của bạn có khai báo phương thức bổ sung sau:

class foobar {
  ...
  const char* bar();
}

Phương thức bar()này không phải là const và chỉ có thể được truy cập từ các giá trị không phải là const.

void func1(const foobar& fb1, foobar& fb2) {
  const char* v1 = fb1.bar();  // won't compile
  const char* v2 = fb2.bar();  // works
}

Ý tưởng đằng sau constlà đánh dấu các phương thức sẽ không làm thay đổi trạng thái bên trong của lớp. Đây là một khái niệm mạnh mẽ nhưng không thực sự có hiệu lực trong C ++. Đó là một lời hứa nhiều hơn là một sự đảm bảo. Và một trong đó thường bị hỏng và dễ dàng bị phá vỡ.

foobar& fbNonConst = const_cast<foobar&>(fb1);

3
Tôi nghĩ rằng câu trả lời là về các phương thức const khác chứ không phải về các đối tượng const.
Mykola Golubyev

Cảm ơn vì "Ý tưởng đằng sau constlà đánh dấu các phương thức sẽ không làm thay đổi trạng thái bên trong của lớp". Đó thực sự là những gì tôi đang tìm kiếm.
kovac 17/03/19

1
@JaredPar điều này có nghĩa là bất kỳ chức năng thành viên nào đại diện cho một hoạt động chỉ đọc nên được đánh dấu là const?
kovac 17/03/19

26

Các const này có nghĩa là trình biên dịch sẽ Lỗi nếu phương thức 'với const' thay đổi dữ liệu nội bộ.

class A
{
public:
    A():member_()
    {
    }

    int hashGetter() const
    {
        state_ = 1;
        return member_;
    }
    int goodGetter() const
    {
        return member_;
    }
    int getter() const
    {
        //member_ = 2; // error
        return member_;
    }
    int badGetter()
    {
        return member_;
    }
private:
    mutable int state_;
    int member_;
};

Các bài kiểm tra

int main()
{
    const A a1;
    a1.badGetter(); // doesn't work
    a1.goodGetter(); // works
    a1.hashGetter(); // works

    A a2;
    a2.badGetter(); // works
    a2.goodGetter(); // works
    a2.hashGetter(); // works
}

Đọc này để biết thêm thông tin


1
Một câu hỏi về các constchức năng thành viên không đề cập đến khả năng thay đổi là không đầy đủ.
IInspectable

13

Câu trả lời của Blair là trên nhãn hiệu.

Tuy nhiên, lưu ý rằng có một mutablevòng loại có thể được thêm vào các thành viên dữ liệu của một lớp. Bất kỳ thành viên nào được đánh dấu có thể được sửa đổi trong một constphương thức mà không vi phạm consthợp đồng.

Bạn có thể muốn sử dụng điều này (ví dụ) nếu bạn muốn một đối tượng nhớ bao nhiêu lần một phương thức cụ thể được gọi, trong khi không ảnh hưởng đến hằng số "logic" của phương thức đó.


10

Ý nghĩa của hàm thành viên Const trong C ++ Kiến thức chung: Lập trình trung gian thiết yếu đưa ra lời giải thích rõ ràng:

Loại của con trỏ này trong hàm không phải là thành viên của lớp X là X * const. Đó là, nó là một con trỏ không đổi đến X không đổi (xem Con trỏ và Con trỏ đến Const [7, 21]). Bởi vì đối tượng mà tham chiếu này không phải là const, nên nó có thể được sửa đổi. Kiểu này trong hàm const thành viên của lớp X là const X * const. Đó là, nó là một con trỏ không đổi đến một hằng số X. Bởi vì đối tượng mà tham chiếu này là const, nó không thể được sửa đổi. Đó là sự khác biệt giữa các hàm const và non-const.

Vì vậy, trong mã của bạn:

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

Bạn có thể nghĩ nó như thế này:

class foobar
{
  public:
     operator int (const foobar * const this) const;
     const char* foo(const foobar * const this) const;
};

thiskhông phải là const. Lý do tại sao nó không thể được sửa đổi là vì nó là một giá trị.
Brian

7

khi bạn sử dụng consttrong chữ ký phương thức (như bạn đã nói const char* foo() const;:) bạn đang nói với trình biên dịch rằng bộ nhớ được trỏ bởi thiskhông thể thay đổi bằng phương thức này ( fooở đây).


6

Tôi muốn thêm điểm sau.

Bạn cũng có thể làm cho nó một const &const &&

Vì thế,

struct s{
    void val1() const {
     // *this is const here. Hence this function cannot modify any member of *this
    }
    void val2() const & {
    // *this is const& here
    }
    void val3() const && {
    // The object calling this function should be const rvalue only.
    }
    void val4() && {
    // The object calling this function should be rvalue reference only.
    }

};

int main(){
  s a;
  a.val1(); //okay
  a.val2(); //okay
  // a.val3() not okay, a is not rvalue will be okay if called like
  std::move(a).val3(); // okay, move makes it a rvalue
}

Hãy cải thiện câu trả lời. Tôi không phải là chuyên gia


1
*thisluôn luôn là một giá trị, ngay cả khi hàm thành viên là rvalue-ref đủ điều kiện và được gọi là một giá trị. Ví dụ .
HolyBlackCat

1
Có, vậy tôi nên cải thiện câu trả lời hiện tại của mình như thế nào?
coder3101

Tôi có nghĩa là những gì để viết bình luận trong khối, điều đó biện minh cho hành vi
coder3101 26/03/19

Cập nhật. Được không
coder3101

2

Các const khóa được sử dụng với các quy định cụ thể chức năng khai báo rằng nó là một hàm thành viên const và nó sẽ không thể thay đổi các thành viên dữ liệu của đối tượng.


1

https://isocpp.org/wiki/faq/const-c Corrness # const-member-fns

" constChức năng thành viên" là gì?

Một chức năng thành viên kiểm tra (chứ không phải đột biến) đối tượng của nó.

Hàm constthành viên được biểu thị bằng consthậu tố ngay sau danh sách tham số của hàm thành viên. Các chức năng thành viên có consthậu tố được gọi là các chức năng thành viên của const const Các hàm thành viên không có consthậu tố được gọi là các hàm thành viên không phải là const

class Fred {
public:
  void inspect() const;   // This member promises NOT to change *this
  void mutate();          // This member function might change *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
  changeable.inspect();   // Okay: doesn't change a changeable object
  changeable.mutate();    // Okay: changes a changeable object
  unchangeable.inspect(); // Okay: doesn't change an unchangeable object
  unchangeable.mutate();  // ERROR: attempt to change unchangeable object
}

Nỗ lực gọi unchangeable.mutate()là một lỗi bắt gặp tại thời điểm biên dịch. Không có không gian thời gian chạy hoặc hình phạt tốc độ cho constvà bạn không cần phải viết các trường hợp thử nghiệm để kiểm tra nó trong thời gian chạy.

Dấu vết consttrên inspect()hàm thành viên nên được sử dụng để có nghĩa là phương thức sẽ không thay đổi trạng thái trừu tượng (hiển thị của máy khách) của đối tượng . Điều đó hơi khác so với việc nói rằng phương thức này sẽ không thay đổi các bit thô của Google. Cấu trúc của đối tượng. Các trình biên dịch C ++ không được phép thực hiện giải thích bit bit bit, trừ khi chúng có thể giải quyết vấn đề răng cưa, điều mà thông thường không thể giải quyết được (ví dụ, một bí danh không phải là có thể tồn tại có thể thay đổi trạng thái của đối tượng). Một cái nhìn sâu sắc (quan trọng) khác về vấn đề răng cưa này: chỉ vào một đối tượng bằng con trỏ-const-const không đảm bảo rằng đối tượng sẽ không thay đổi; nó chỉ hứa hẹn rằng đối tượng sẽ không thay đổi thông qua con trỏ đó .

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.