143

作为一个新手 C++ 程序员,有一些结构对我来说仍然很模糊,其中之一是const. 您可以在如此多的地方使用它,并具有如此多不同的效果,这对于初学者来说几乎是不可能的。一些 C++ 大师会永远解释各种用途以及是否和/或为什么不使用它们吗?

4

4 回答 4

107

试图收集一些用途:

将一些临时绑定到引用到常量,以延长其生命周期。引用可以是一个基 - 它的析构函数不需要是虚拟的 - 仍然调用正确的析构函数:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

解释,使用代码:

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); }

这个技巧在 Alexandrescu 的 ScopeGuard 实用程序类中使用。一旦临时超出范围,就会正确调用 Derived 的析构函数。上面的代码遗漏了一些小细节,但这是最重要的。


使用 const 告诉其他方法不会改变这个对象的逻辑状态。

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

将 const 用于写时复制类,以使编译器帮助您决定何时以及何时不需要复制。

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

解释:只要原始对象和复制对象的数据保持不变,您可能希望在复制某些内容时共享数据。一旦其中一个对象更改了数据,您现在需要两个版本:一个用于原始版本,一个用于副本。也就是说,您在写入时复制到任一对象,这样它们现在都有自己的版本。

使用代码

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

上面的代码片段在我的 GCC 上打印了相同的地址,因为使用的 C++ 库实现了 copy-on-write std::string。两个字符串,即使它们是不同的对象,它们的字符串数据共享相同的内存。制作b非常量会优先使用非常量版本,operator[]而 GCC 将创建后备内存缓冲区的副本,因为我们可以更改它并且它一定不会影响a!

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

对于复制构造函数从 const 对象和临时对象进行复制

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

用于制作无法更改的常量

double const PI = 3.1415;

用于通过引用而不是按值传递任意对象- 以防止可能昂贵或不可能的按值传递

void PrintIt(Object const& obj) {
    // ...
}
于 2009-01-18T17:33:21.693 回答
31

C++ 中 const 的主要用途有 2 个。

常量值

如果一个值是在其生命周期内不会(或不应)更改的变量、成员或参数的形式,则应将其标记为 const。这有助于防止对象发生突变。例如,在以下函数中,我不需要更改传递的 Student 实例,因此我将其标记为 const。

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

至于你为什么要这样做。如果您知道基础数据无法更改,那么推理算法会容易得多。"const" 有帮助,但不保证会实现。

显然,将数据打印到 cout 不需要太多思考:)

将成员方法标记为 const

在前面的示例中,我将 Student 标记为 const。但是 C++ 怎么知道在 student 上调用 GetName() 方法不会改变对象呢?答案是该方法被标记为 const。

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

将方法标记为“const”会做两件事。主要是它告诉 C++ 这个方法不会改变我的对象。第二件事是所有成员变量现在都将被视为被标记为 const。这有助于但不会阻止您修改类的实例。

这是一个非常简单的示例,但希望它能帮助回答您的问题。

于 2009-01-18T17:34:43.530 回答
17

注意理解这 4 个声明之间的区别:

以下 2 个声明在语义上是相同的。您可以更改ccp1和 ccp2 指向的位置,但不能更改它们指向的内容。

const char* ccp1;
char const* ccp2;

接下来,指针是 const,所以为了有意义,它必须初始化为指向某个东西。你不能让它指向别的东西,但是它指向的东西是可以改变的。

char* const cpc = &something_possibly_not_const;

最后,我们将两者结合起来——所以被指向的东西不能被修改,指针也不能指向其他任何地方。

const char* const ccpc = &const_obj;

顺时针螺旋规则可以帮助解开声明http://c-faq.com/decl/spiral.anderson.html

于 2009-01-22T06:25:04.490 回答
3

作为一个小提示,正如我在这里所读到的,注意到这一点很有用

const适用于它最左边的任何东西(除非那里没有任何东西,在这种情况下它适用于它最右边的任何东西)。

于 2017-05-11T12:00:37.967 回答