0 引言
《C和指针》中对&操作符,*操作符和const修饰词有一些基本的介绍,这些介绍精确戳中了其本质含义,对于涉及到这些操作符的语法的理解很有帮助。因此写作这篇博文帮助后续的理解。
reference:
- 《C和指针》
- https://stackoverflow.com/questions/3141087/what-is-meant-with-const-at-end-of-function-declaration
- https://www.cnblogs.com/ghjnwk/p/15555186.html
- http://www.mathcs.emory.edu/~cheung/Courses/561/Syllabus/3-C/ref-deref.html reference and dereference
- http://duramecho.com/ComputerInformation/WhyHowCppConst.html
- https://stackoverflow.com/questions/751681/meaning-of-const-last-in-a-function-declaration-of-a-class
1 基本含义
(1)&操作符 (reference operator) and also (address of operator):
reference operator & operates on a (single) variable name and return the address of that variable.
&操作符产生他的操作数的地址。通常用于两个地方:
- 引用声明: 在引用声明中,& 不应当被理解为取地址,而应当和变量类型int一起被看作是一个整型引用。
int i = 17;
int& r = i; ///< 意义: r 是一个初始化为 i 的整型引用
- 给指针赋值
int *a;
int b = 100;
a = &b;
(2)*操作符(dereference operator): the dereference operator * operates on an address (an unsigned integer) and return the value stored at that address.
声明一个指针的含义如下:对 int *a; 来说,这条语句表示表达式 *a产生的结果类型是int. 知道了*操作符执行的是间接访问操作之后,我们可以推断a肯定是一个指向int的指针。 eg:
- int *b, c, d; ///< 声明一个整型指针变量b和两个整型变量c, d
- int *b, *c, *d; ///< 声明三个整型的指针变量
- char *message = "hello world"; ///< 这条语句把message声明为一个指向字符的指针,并用字符串常量中第一个字符的地址对该指针进行初始化。 等价于以下:
- char *message; message = "hello world";
初始化一个指针变量的基本方法
- int *a = &b;
- std::cout << "int value at address 136760 = " << *(int* ) 136760 << std::endl; ///< (type* ) tells compiler how much memory need to be allocated.
(3)const修饰词和*指针
- int const a = 15; equals const int a = 15; 目前选择const int a = 15; 作为本人的常用形式
- const int *pci; ///< 含义: pci是一个指针,当解引用(*)pci时,得到一个const int类型的值,表明pci是一个指向const int的指针。
- const int *const pci; ///< 含义: pci是一个指针,当解引用(*)pci时,得到一个const int 类型的值,表明pci是一个指向const int的指针。同时,pci被const修饰,因此pci本身的值和它指向的那个值都不能被修改。
- #define 和const: #define指令是另一种创建名字变量的机制。 例子:
- #define MAX_ELEMENTS 50
- const int max_element = 50;
在这种情况下, 使用#define比使用const变量更好。因为只要允许使用字面值变量的地方都可以使用前者,比如声明数组长度。而const变量只能用于允许使用变量的地方。
2 常量引用的基本规则以及 (const Var&, Var is a class name)
基本规则1:如果一个普通函数的参数是一个const reference object,那么它将只能够调用被const修饰的类成员函数. eg:
#include <iostream> class Var { public: Var(const int &size) { d_size = size; } int getSize() { return d_size; } private: int d_size; }; void noChangeSize(const Var &aa) { std::cout << "aa.size = " << aa.getSize() << std::endl; } void TestConstRef() { Var aa(77); std::cout << "aa.size = " << aa.getSize() << std::endl; noChangeSize(aa); } int main() { TestConstRef(); return 0; }
compiling output:
test.cpp: In function ‘void noChangeSize(const Var&)’: test.cpp:15:45: error: passing ‘const Var’ as ‘this’ argument of ‘int Var::getSize()’ discards qualifiers [-fpermissive] std::cout << "aa.size = " << aa.getSize() << std::endl;
下面将另起一章介绍const修饰函数
3 用const修饰函数
examples:
///< case1: const at the beginning const int MyClass::showName(string id){ ... } ///< case2: const at the end int MyClass::showName(string id) const{ ... } ///< case3: const both at the beginning and at the end const int MyClass::showName(string id) const{ ... }
(1)const 在函数的名字前面
在case1中,表示MyClass::showName的返回值是一个const int类型的值。此时有两种调用方式
- const int id = m.showName("id"); ///< 在这种情况下,被从返回值copy过来的时候,id被初始化为跟返回值一样的类型,不能被修改
- int id = m.showName("id"); ///< 在这种情况下,id被一个const int 类型的返回值初始化为int类型,此时当然可以被修改
(2)const 在函数的尾巴那里
在case2中,表示MyClass::showName在操作MyClass的类成员变量是,不应该对其中的变量进行修改。 此处case2的写法相当于传给showName一个const类型的this指针。写作如下:
- case2 equals
int MyClass::showName(const MyClass *this, string id) const{ ...///< class member of this should not be changed by this function. }
但是也有例外的情形,当class member被mutable修饰的时候,是可以被以const结尾修饰的function修改值的
#include <iostream> class Var { public: Var(const int &size) { d_size = size; } int getSize() { return d_size; } void setSize(const int& size) const { d_size = size; } private: mutable int d_size; }; void noChangeSize(Var* aa) { aa->setSize(89); ///< not supported. std::cout << "aa.size = " << aa->getSize() << std::endl; } void TestConstRef() { Var aa(77); std::cout << "aa.size = " << aa.getSize() << std::endl; noChangeSize(&aa); } int main() { TestConstRef(); return 0; }
4 ::
(1) :: is known as the scope resolution operator. eg:
a. std::cout , std::cin are defined within std , so they have to qualify their names with std::
b. foo::foo() {}; class is similar as namespace, 用法类似。
(2) :: is used to dereference scopes.
const int x = 5;
namespace foo {
const int x = 0;
}
int bar() {
int x = 1;
return x;
}
struct Meh {
static const int x = 2;
}
int main() {
std::cout << x; // => 5
{
int x = 4;
std::cout << x; // => 4
std::cout << ::x; // => 5, this one looks for x outside the current scope
}
std::cout << Meh::x; // => 2, use the definition of x inside the scope of Meh
std::cout << foo::x; // => 0, use the definition of x inside foo
std::cout << bar(); // => 1, use the definition of x inside bar (returned by bar)
}