C++常量成员函数返回一个常量指针,但返回的指针是哪种类型的常量?

47

请原谅我如果这个问题已经被问过了,但我该如何在c++中创建一个返回指针的成员函数呢,以下是几种情况: 1. 返回的指针是常量,但内部的内容可以修改。 2. 内部的内容是常量,但返回的指针可以修改。 3. 内部和指针都不能被修改。

应该像这样:

  1. int *const func() const
  2. const int* func() const
  3. const int * const func() const

我看过的所有教程都没有涉及到这个区别。

附注: 如果我的方法声明为const,那么教程说我声明了不会修改参数。但在参数是指针的情况下,这对我来说不够清晰。我的参数需要像这样吗:

a. void func(const int* const x) const;
b. void func(const int* x) const;
c. void func(const int* const x) const;


意识到const成员函数与修改参数无关,更多的是用于修改类变量。 - Jor
5个回答

87
我不知道你读了哪本书,但是如果你将一个方法标记为const,则意味着this将是const MyClass*类型,而不是MyClass*类型,这又意味着您不能更改未声明mutable的非静态数据成员,也不能在this上调用任何非const方法。

现在谈到返回值。

1. int * const func() const

函数是常量,返回的指针是常量,但“内部垃圾”可以被修改。但是,我认为返回const指针没有意义,因为最终函数调用将是一个右值,而非类类型的右值无法是const的,这意味着const会被忽略。

2. const int* func() const

这是一个有用的东西。“内部垃圾”不能被修改。

3. const int * const func() const

从语义上讲,与2几乎相同,由于1中的原因。

希望对你有所帮助。


1
我认为你在回复中提供了一些有用的信息,但我的问题仍然存在。如果我将一个成员方法声明为const,那么我的方法签名需要包含哪些内容是必须的?如果我没有理解你的意思,我很抱歉。 - Jor
1
@Jor:在函数签名的其余部分上,您不需要做任何限制。唯一需要注意的是,在const方法中,this是指向const的指针,因此如果您通过指针或引用返回成员,则返回值必须是对const类型的引用或指针,否则您将不得不进行潜在危险的const_cast操作。 - CB Bailey
1
@Jor:也就是说,返回类型取决于你要返回什么。如果你要返回一个类成员的地址,在一个const成员函数中,那么该成员在const成员函数中是const的,所以你需要选择选项(2)。 - Ben Voigt

19

有些情况下,使用const并没有太多意义。

假设你有以下函数:

void myFunction (const int value);

const关键字告诉编译器在函数内该值不能改变。对于调用者来说,这个信息没有任何价值。函数本身决定如何处理该值。对于调用者来说,以下两个函数定义表现完全相同:

void myFunction (const int value);
void myFunction (int value);

因为传递的是值,这意味着函数无论如何都会得到一个本地副本。

另一方面,如果参数是引用或指针,情况就变得非常不同。

void myFunction (const MyClass &value);

这告诉调用者该值是按引用传递的(在幕后实际上是指针),但调用者承诺不更改该值。指针也是如此:

void myFunction (const MyClass *value);

因为性能原因,我们传递了 MyClass 的指针,但是该函数保证不会修改该值。

如果我们写下如下代码:

void myFunction (MyClass * const value);

那么我们回到了最初的情况。myFunction获取一个指针,该指针通过值传递,并且是const类型的。由于MyFunction获得了指针值的副本,因此对于调用者来说,它是否为const并不重要。最重要的是,myFunction可以更改value的内容,因为指针变量本身是const,但其中的内容则不是。

返回值也是如此:

const double squareRoot(double d);

这没什么意义。squareRoot返回一个const double,但由于这是通过值传递的,因此需要复制到我的本地变量中,我可以随意对其进行操作。

另一方面:

const Customer *getCustomer(char *name);
告诉我getCustomer返回一个指向客户的指针,我不被允许更改该客户的内容。
实际上,最好也将char指针内容设置为const,因为我不希望该函数更改给定的字符串。
const Customer *getCustomer(const char *name);

好的,我开始理解Patrick了,并且意识到const方法与不修改类成员有关..所以忘记它吧。但是我喜欢你的答案,也许你能回答这些问题:在“const Client * getCustomer(char * name);”中,您说调用者无法修改结果。 但是,如果他们将指针复制到另一个指针上,他们是否可以通过返回的变量修改内容? - Jor
这里还有一个问题。一个const方法不能返回指向其类成员的指针,除非它是一个const指针。但是它必须是哪种类型的const指针呢?1:const *int 2:const * const int 3:int * const - Jor
如果您将“const Customer *getCustomer()”函数的返回值分配给“Customer *”变量,则可以更改Customer的内容。但是,C++编译器在此赋值时会出现错误,因为通常不允许将const指针分配给非const指针,因为这授予了您“额外的权限”(即:能够更改Customer的内容)。然而,在某些情况下(特别是处理旧的纯C代码时),您可能需要规避此问题,然后应使用const_cast。 - Patrick
在你的第二个问题中:返回值必须是“const *int”。将const放在int之前没有意义,因为调用者无论如何都会得到指针的副本,并告诉它不能更改由函数返回的“临时”指针也没有意义。 - Patrick
@Jor 提示:假设您没有强制转换常量,编译器将验证您是否遵守常量声明 - 在函数中尝试修改变量(它们指向的内容以及其内容)可能是最简单的方法,因为有不同的签名。 - justin

7

int *const func() const

const 除了一些特殊情况外,你看不到它的作用。

  • 获取 func 的地址。
  • 在 C++0x 中,使用函数调用语法直接调用 func 作为 decltype 操作数,将返回 int * const

这是因为你返回的是纯指针值,也就是说并没有存储在指针变量中的指针值。这些值并没有被 const 限定,因为无论如何都无法更改它们。即使去掉 const,你也不能说 obj.func() = NULL;。在两种情况下,表达式 obj.func() 的类型都是 int*,且是不可修改的(很快会有人引用标准并提出“右值”一词)。

因此,在使用返回值的上下文中,你无法发现任何区别。只有在引用声明或整个函数本身时才会注意到区别。

const int* func() const

如果函数主体类似于 return &this->intmember;,那么通常会这样做。它不允许通过 *obj.func() = 42; 来更改 int 成员。

const int * const func() const

这只是前两种情况的组合 :)


2

返回指向const的指针非常有意义,但返回const指针(不可修改)通常没有太多价值(尽管有人说它可以防止用户错误或增加编译器优化)。

这是因为返回值属于函数的调用者,即它们自己的副本,因此如果他们修改它(指向其他内容),那么实际上并不重要。但是,内容并不属于调用者,函数的实现者可能会制定一个只读信息的约定。

const成员函数承诺不改变类的状态,尽管编译器在现实中不一定执行该操作。我在这里所指的并不是const_cast或可变成员,而是如果您的类本身包含指针或引用,则const成员函数会将指针转换为常量指针,但不会将它们转换为指向const的指针,同样,您的引用也不会转换为指向const的引用。如果这些是您类的组成部分(这样的组成部分通常由指针表示),则您的函数可以更改它们的状态。

可变成员的存在有利于使您的类能够更改它们而不更改内部状态。这通常适用于:

  • 需要锁定的互斥量,即使是用于读取。
  • 懒加载的数据,即第一次访问时填充。
  • 引用计数对象:如果它有另一个查看者,则要增加引用计数,因此您修改其状态只是为了读取它。

const_cast通常被认为是一种“hack”,并且通常在其他人没有正确编写代码的情况下进行。但在以下情况下可能会有价值:

  • 多个重载其中一个是const而另一个不是const,并且const返回const引用而非const返回非const引用,但除此之外它们是相同的。复制代码(如果不是简单的数据成员获取)不是一个好主意,因此使用另一个实现一个并使用const_cast绕过编译器。

  • 您想特别调用const重载,但具有非const引用。先将其转换为const。


0

const方法防止您修改成员。对于指针,这意味着您无法重新分配指针。您可以随心所欲地修改指向指针的对象。

由于指针是按值(副本)返回的,因此调用者无法使用它来修改类的指针成员。因此,在返回值中添加const不会增加任何内容。

如果您要返回指针的引用,则情况就不同了。现在,如果指针不是const,这意味着一个没有修改值权限的函数正在授予调用者此权限。

例如:

class X
{
    int* p;
public:
    int* get_copy_of_pointer() const //the returned value is a copy of this->p
    { 
        *p = 42;  //this being const doesn't mean that you can't modify the pointee
        //p = 0;  //it means you can't modify the pointer's value
        return p; 
    }
    int* const& get_reference_to_pointer() const //can't return a reference to non-const pointer
    {
        return p;
    }
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接