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