假设我们有一个名为object的类。
int main(){
object a;
const object* b = &a;
(*b);
}
问题: b是指向const的指针,但它指向的对象实际上不是常量对象。我的问题是,当我们取消引用指针“b”时,为什么我们得到一个常量对象而不是它实际指向哪个是正常的非常量对象。
答案 0 :(得分:23)
因为内置解除引用运算符*
在C ++中的工作方式。如果取消引用类型为T *
的指针,则会得到类型为T
的左值。在您的情况下,T
为const object
。
*
运算符,而不是指针本身关心(或知道)指针指向的对象实际上是非常量的。在C ++语言使用的静态类型的概念中,它不可能是任何其他方式。
间接的第一个(和更深层次)级别的const限定的全部目的是为您提供为对象创建限制性访问路径的能力。即通过创建指向const的指针,即使指针对象不是常量,你也故意并且自愿地阻止自己(或其他人)修改指针对象。
答案 1 :(得分:12)
表达式的类型仅基于表达式中变量的声明类型,它不依赖于动态运行时数据。例如,你可以写:
object a1;
const object a2;
const object *b;
if (rand() % 2 == 0) {
b = &a1;
} else {
b = &a2;
}
(*b);
*b
的类型是在编译时确定的,它不依赖于运行程序时rand()
返回的内容。
由于b
被声明指向const object
,因此*b
的类型是什么。
答案 2 :(得分:6)
因为const
关键字表示“您无法修改该对象”而不是“无法修改该对象”。
当您将对象传递给某个函数以仅使用对象的值但是不能修改它时,这很有用:
// We expect PrintMyString to read the string
// and use its contents but not to modify it
void PrintMyString(const char *string);
void myfunction(int number)
{
// build and print a string of stars with given length
if(number>0 && number<=16]
{
char string[17];
int i;
for(i=0, i<number; i++)
string[i] = '*';
string[number] = '\0';
PrintMyString(string);
}
}
函数PrintMyString
将获得一个字符数组,但该数组将作为“只读”传递给该函数。数组在拥有函数中当然是可修改的,但PrintMyString
只能读取它得到的内容而不会改变内容。
答案 3 :(得分:2)
const object* b = &a;
这意味着: b是指向const(只读)对象的指针
对const
a
的{{1}}没有任何说明。这只是意味着b
无权修改a
。
也称为低级 const
。
<小时/> 与上述相反,顶级
const
是指针本身为const
的时候。
object *const b = &a; // b is a const pointer to an object
b = &some_other_object; // error - can't assign b to another object
// since b is a const pointer
*b = some_value; // this is fine since object is non-const
答案 4 :(得分:2)
这里还有很多其他的答案,但是我想发布这个,因为我觉得他们中的很多都提供了太多的细节,偏离主题,或者承担了OP几乎肯定没有的C ++术语知识&# 39;有。我认为这对他们职业生涯的类似阶段的OP和其他人没有帮助,所以我将试图切断其中的一部分。
情况本身实际上非常简单。声明:
const SomeClassOrType *p = ...
简单地说无论p指向什么都无法通过该指针进行修改,这对于确保通过p获得对该对象的访问权限的代码在它不应该被修改时是有用的。 。它最常用于参数声明,以便函数和方法知道它们是否可以修改传入的对象(*)。
它根本没有说明实际指向的内容。指针本身只是一个简单的灵魂,它不会随身携带那些信息。它只知道就指针而言,指向的对象可以从中读取但不能写入。
具体例子(从CiaPan偷来):
void PrintString (const char *string);
char string [] = "abcde";
const char *p = string;
PrintString (p);
void PrintString (const char *ptr_to_string)
{
ptr_to_string [0] = 0; // oops! but the compiler will catch this, even though the original string itself is writeable
}
您当然可以直接将string
传递给PrintString
,但差别相同,因为该参数也被声明为const。
另一种看待这种情况的方法是const
是程序员提供的信息,用于帮助编译器在编译时检查代码的正确性/一致性。它允许编译器捕获上面的错误,并可能执行更好的优化。当你实际运行代码时,它就是历史。
(*)现代成语很可能是使用const引用,但我不想把它放到主要讨论中来混淆水域。
答案 5 :(得分:1)
简单地说,
对于普通的poiters,一元运算符*
的结果是对指向类型的引用。
因此,如果指向的类型是const限定的,则结果是对常量的引用。
对常量的引用可以绑定到任何对象,甚至绑定到相同类型的可变对象。当你将一个constref绑定到一个值时,你承诺不会通过这个refence临时修改那个值(你仍然可以显式地将它强制转换为非const引用)。
同样,const-pointers可以指向仍然可以修改的对象。
答案 6 :(得分:1)
写作
const object* b = &a;
您声明b
是*
类型const
的指针(object
),然后您可以为其指定a
的地址。 a
的类型为object
(但不是const);您可以使用非const object
的地址代替const object
的地址。
但是,当您取消引用*
b时,编译器只能根据您的声明进行操作 - 因此*b
是const object
(但您仍然可以修改a
喜欢,所以要小心认为对象b
指向不能改变 - 它不能通过b
改变
答案 7 :(得分:0)
const object* b = &a;
b会将其指向的内容视为const
,即无法更改a
object* const b = &a;
b本身为const
,即它无法指向其他object
地址,但可以更改a
答案 8 :(得分:0)
因为在运行时它可能是一个const对象,这意味着在取消引用上执行的任何操作都必须与const兼容。忽略这样一个事实,即在你的例子中它被指向一个非const对象并且不能指向其他任何东西,这种语言不能像那样工作。
答案 9 :(得分:0)
这是一个具体的例子,说明它是什么样的。假设您声明:
int a[] = {1, 2, 3};
constexpr size_t n_a = sizeof(a)/sizeof(a[0]);
extern int sum( const int* sequence, size_t n );
sum_a = sum( a, n_a );
现在,您在另一个模块中实施sum()
。它不知道你指向的原始对象是如何声明的。编写一个用这些信息标记指针的编译器是可能的,但是由于一些很好的理由,今天没有实际使用的编译器.¹
内部sum()
,可能位于无法使用整个程序优化重新编译的共享库中,您所看到的只是指向无法更改的内存的指针。实际上,在某些实现中,尝试通过指向const
的指针进行写操作可能会使程序崩溃并出现内存保护错误。并且将一块内存重新解释为其他类型的能力对于C / C ++很重要。例如,memset()
或memcpy()
将其重新解释为任意字节数组。因此sum()
的实现无法告诉其指针参数的来源。就它而言,它只是指向const int[]
的指针。
更重要的是,函数的契约表明它不会通过该指针修改对象。它可以简单地抛弃const
限定符,但那将是逻辑错误。如果你正在声明一个指针const
,那就像是在你的枪上使用安全装置:你想要编译器阻止你在脚下射击。
¹包括从指针中提取地址的额外指令,用于存储它们的额外内存,与架构的标准调用约定的兼容性,打破了许多现有代码,这些代码假定long
之类的东西能够容纳指针,没有任何好处。
²mutable
数据成员的迂腐例外。