`###### stage_0 基本
指針 - 變量。存儲(chǔ)的是一個(gè)地址
引用
- 是某個(gè)變量的別名。引用和原變量在內(nèi)存的同一個(gè)區(qū)域。
<-----這是不是讓人想起了什么,軟鏈接硬鏈接區(qū)別是啥? ---硬鏈接也是別名啊親
那引用本身有占據(jù)空間嗎? 這個(gè)我認(rèn)為應(yīng)該是要看編譯器的,標(biāo)準(zhǔn)里并沒(méi)有說(shuō)要如何實(shí)現(xiàn)引用??,比如我可能自己實(shí)現(xiàn)一個(gè)符合標(biāo)準(zhǔn)的編譯器,其中的引用我就給他編譯成一個(gè)指針,那這樣你說(shuō)占空間還是不占?但輸出引用的地址一定是和原始變量一致的.
- 引用類型必須初始化。否則報(bào)錯(cuò)類似
error: ‘a(chǎn)_ref’ declared as reference but not initialized - 有const指針,沒(méi)有const引用;
int a(0);
int & const ref = a;//error: 'const' qualifier may not be applied to a reference
指針常量/常量指針
就是以
*為分界,看const跟誰(shuí)比較近就是修飾誰(shuí).
int *const ptr; //指針常量,ptr本身不能被更改,*ptr可以被更改
const int *ptr1; //常量指針,ptr本身可以更改,指向的內(nèi)容不能更改哦.
int const *ptr2; //同上,常量指針
- ref類型還不能重新被賦值。
原因是Bjarne(包括我也)覺(jué)得如果能重新綁定引用(或者叫重新指代),那么語(yǔ)法會(huì)變得很奇怪.就像胡蘿卜汁加咖啡一樣.
請(qǐng)check一下這個(gè)<a href=http://stackoverflow.com/questions/9293674/can-we-reassign-the-reference-in-c>thread</a>和effective c++37頁(yè)
//編譯通過(guò),但不代表這就是重新指代
string k("dog1");
string k2("dog2");
string &c = k;
c = k2; //<--------------這句實(shí)際上的效果類似于 k = k2,只是改變了值,并沒(méi)有改變c與k的綁定. 無(wú)法想象如何去重新綁定.
printf("%p %p %p\n", &c , &k, &k2); //實(shí)際上c依然引用k
- 不存在多級(jí)引用,但有多級(jí)間接指針.
int a(100);
int* ptr = &a;
int** ptr_ptr = &ptr;
int &ref = a;
int &ref2 = ref;//這并不是引用的引用,這還是a的引用.
//所以引用的語(yǔ)法我覺(jué)得就是在說(shuō)明這問(wèn)題,他是平坦的,只是個(gè)別名,并不存在間接關(guān)系.
printf("%p %p\n%p %p\n", ptr, *ptr_ptr, &ref, &ref2);
-
sizeof操作符,sizeof引用對(duì)象可以得到原對(duì)象的大小,而sizeof指針只能得到指針大小.
<---------------又想起啥來(lái)了!
//確定數(shù)組元素個(gè)數(shù)的宏:
#define Num_of_Arr(A) (sizeof(A)/sizeof(A[0]))
int A[100];
int (&refarr) [100]= A;//必須要指定大小
//int &refarr [100] = A; // error: declaration of ‘refarr’ as array of references
printf("%lu %lu %lu\n", sizeof(A), sizeof(refarr), sizeof(A)/sizeof(A[0]));
引用數(shù)組與數(shù)組的引用
沒(méi)有引用數(shù)組,編譯不過(guò)。
對(duì)數(shù)組的引用,需要注意,Type (&ref_name) [300] = Arr;要加括號(hào).
----------->又想起啥了.函數(shù)指針,指向數(shù)組的指針也都是這樣,需要加括號(hào),因?yàn)?code>[]的優(yōu)先級(jí)高于*引用的自增與指針的自增(這應(yīng)該很容易推出的吧)
<a href=http://www.cnblogs.com/dolphin0520>參考</a>
stage_1 引用的一些其他
- 關(guān)于函數(shù)參數(shù)傳入方式 - 值/引用/地址
- 函數(shù)參數(shù)以值傳入時(shí):
- 參數(shù)類型是與傳入的原始對(duì)象類型一致,則會(huì)引發(fā)復(fù)制構(gòu)造(隱式);
- 參數(shù)類型不一致,且存在該類型的類型轉(zhuǎn)換構(gòu)造函數(shù),則會(huì):1. 通過(guò)該類型轉(zhuǎn)換構(gòu)造函數(shù)生成一個(gè)臨時(shí)對(duì)象,2. 通過(guò)復(fù)制構(gòu)造函數(shù)復(fù)制給參數(shù).但是運(yùn)行時(shí)的表現(xiàn)是只調(diào)用了轉(zhuǎn)換構(gòu)造函數(shù). 因?yàn)?del>public復(fù)制構(gòu)造函數(shù)被優(yōu)化.(編譯器優(yōu)化,具體如何優(yōu)化的不得而知,有人說(shuō)是public復(fù)制構(gòu)造函數(shù)被優(yōu)化,有人說(shuō)是堆棧優(yōu)化)
1和2都是隱式調(diào)用,假如1/2中的構(gòu)造函數(shù)任意一者加了explicit聲明,編譯都會(huì)失敗.
(見(jiàn)附1) - 淺拷貝問(wèn)題: 當(dāng)函數(shù)參數(shù)以值傳入時(shí),會(huì)引發(fā)復(fù)制構(gòu)造,如果只是編譯器自動(dòng)生成的,則只是對(duì)各個(gè)成員進(jìn)行字面上的復(fù)制,假如成員中有指針并且構(gòu)造/析構(gòu)會(huì)對(duì)申請(qǐng)釋放一段內(nèi)存,則很可能會(huì)出現(xiàn)二次釋放問(wèn)題。淺拷貝的解決方式:1. 使用引用傳遞,2.重寫(xiě)復(fù)制構(gòu)造函數(shù),改為深拷貝.
(見(jiàn)附2)
- 函數(shù)參數(shù)以引用傳遞時(shí):
- 參數(shù)類型一致: 不會(huì)引發(fā)構(gòu)造,參數(shù)是原始對(duì)象的引用(一個(gè)別名).
- 參數(shù)類型不一致:1. 會(huì)生成一個(gè)臨時(shí)對(duì)象(隱式類型轉(zhuǎn)換,可被explicit禁止)2. 參數(shù)類型必須帶有const修飾.(對(duì)臨時(shí)對(duì)象的引用)
(見(jiàn)附3)
- 關(guān)于函數(shù)返回值的方式 - 值/引用/地址(主要是對(duì)返回值的生命周期有些疑惑)
- 以值返回:情況與上面說(shuō)的一樣,會(huì)引發(fā)復(fù)制構(gòu)造(?),會(huì)有淺拷貝的問(wèn)題.
- 原理上來(lái)講是需要一個(gè)臨時(shí)變量來(lái)存返回值:
- 從返回值說(shuō)起(以局部對(duì)象返回):返回值是函數(shù)體內(nèi)部的變量,在函數(shù)返回后已經(jīng)出作用域,需要析構(gòu).
- 臨時(shí)對(duì)象,在返回值析構(gòu)之前用一個(gè)臨時(shí)對(duì)象暫存該返回值(調(diào)用復(fù)制構(gòu)造),該臨時(shí)對(duì)象的生命周期在調(diào)用該函數(shù)的行后結(jié)束.
- 如果這行存在賦值或復(fù)制初始化等情況,則會(huì)調(diào)用復(fù)制構(gòu)造函數(shù)從臨時(shí)對(duì)象復(fù)制.
- 實(shí)現(xiàn)上則存在RVO(Return Value Optimization)的情形.編譯器優(yōu)化了1/2/3,在調(diào)用行是初始化時(shí)直接把需要初始化的對(duì)象搞成了局部對(duì)象(該局部對(duì)象并沒(méi)有析構(gòu)),在調(diào)用行是賦值時(shí)直接從返回值復(fù)制. 省略了臨時(shí)對(duì)象.
(附4中詳細(xì)討論)
- 原理上來(lái)講是需要一個(gè)臨時(shí)變量來(lái)存返回值:
- 以引用返回:不會(huì)造成臨時(shí)對(duì)象的復(fù)制.但以引用返回實(shí)在很尷尬.
- 引用返回的方式一般是把返回值所在變量以引用參數(shù)傳遞進(jìn)函數(shù),函數(shù)內(nèi)部對(duì)其進(jìn)行修改后返回該引用.
- 在類的operator方法重載中經(jīng)常以*this方式返回一個(gè)引用.
(附6)
附:聲明性修飾——僅在函數(shù)聲明時(shí)寫(xiě)該關(guān)鍵字即可,定義時(shí)不加.
static
explicit
區(qū)別:inline關(guān)鍵字必須加在函數(shù)定義之前,只加在聲明處不起作用.(甚至?xí)l(fā)編譯器警告g++4.8)
附1:值傳遞
#include <cstdio>
class base{
public:
base();
~base();
base(int); //explicit base(int);foo(90): error: could not convert ‘90’ from ‘int’ to ‘base’
base(const base &);//explicit base(const base &); foo(k): error: no matching function for call to ‘base::base(base&)’ foo(90): error: no matching function for call to ‘base::base(base)’
private:
int val;
};
inline base::base():val(0){ }
inline base::~base(){
printf("I[%p] am dying.\n", this);
}
inline base::base(const base & b) : val(b.val){
printf("I[%p] am copied from %d,%p\n", this, b.val, &b);
}
inline base::base(int k):val(k){
printf("I[%p] am init from <int>\n", this);
}
int foo(base b){
//do nothing.
printf("b - %p\n", &b);
}
int main()
{
base k(100);
printf("====\n");
foo(k);//<1>
printf("====\n");
printf("====\n");
foo(90);//<2>
printf("====\n");
return 0;
}
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
I[0x7ffebc53dd00] am init from <int>
====
I[0x7ffebc53dd10] am copied from 100,0x7ffebc53dd00
b - 0x7ffebc53dd10
I[0x7ffebc53dd10] am dying.
====
====
I[0x7ffebc53dd20] am init from <int>
b - 0x7ffebc53dd20
I[0x7ffebc53dd20] am dying.
====
I[0x7ffebc53dd00] am dying.
附2:淺拷貝
#include <iostream>
#include <assert.h>
#include <string.h>
#include <cstdio>
class base{
public:
base(int size);
~base();
int in(const void *p, int size);
int out(void *p);
private:
char *buf_ptr;
int size_;
int used_;
};
base::base(int size) : buf_ptr(NULL), size_(size), used_(0){
buf_ptr = new char[size];
}
base::~base(){
delete [] buf_ptr;
}
int base::in(const void *p, int size)
{
if(size > size_ || used_){
return -1;
}
memcpy(buf_ptr ,p , size);
buf_ptr[size] = '\0';
used_ = size + 1;
return 0;
}
int base::out(void *p)
{
if(!p || !used_){
return -1;
}
memcpy(p ,buf_ptr, used_);
return 0;
}
void fuck(base &b)
{
char buf[256];
if(0 == b.out(buf))
std::cout<<buf<<std::endl;
printf("ref's address %p\n", &b);
}
void fuck2(base b)
{
char buf[256];
if(0 == b.out(buf))
std::cout<<buf<<std::endl;
printf("ref's address %p\n", &b);
}
int main()
{
base fff(256);
const char* str = "today is a good day!";
fff.in(str, strlen(str));
printf("obj's address %p\n", &fff);
fuck2(fff); //fuck(fff); <----------
return 0;
}
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
obj's address 0x7fff98c17660
today is a good day!
ref's address 0x7fff98c17670
*** Error in `./test': double free or corruption (top): 0x0000000000a7a010 ***
Aborted (core dumped)
注釋處若改成fuck(fff);
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
obj's address 0x7ffd5f375dd0
today is a good day!
ref's address 0x7ffd5f375dd0
附3:對(duì)臨時(shí)對(duì)象的引用
#include <cstdio>
class base{
public:
base();
~base();
base(int);
base(const base &);//<3>explicit base(const base &);
private:
int val;
};
inline base::base():val(0){ }
inline base::~base(){
printf("I[%p] am dying.\n", this);
}
inline base::base(const base & b) : val(b.val){
printf("I[%p] am copied from %d,%p\n", this, b.val, &b);
}
inline base::base(int k):val(k){
printf("I[%p] am init from <int>\n", this);
}
int foo(base b){
//do nothing.
printf("b - %p\n", &b);
}
int foo2(base & x)//<2> int foo2(base const & x)
{
printf("x - %p\n", &x);
}
int main()
{
base k(100);
foo2(k);//<1>foo2(90);
return 0;
}
沒(méi)做注釋處替換:
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
I[0x7ffd5f840050] am init from <int>
x - 0x7ffd5f840050
I[0x7ffd5f840050] am dying.
替換注釋<1>所在行,其他地方不變. 原因是對(duì)臨時(shí)對(duì)象必須使用const引用
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ g++ para.cc -o test
para.cc: In function ‘int main()’:
para.cc:34:9: error: invalid initialization of non-const reference of type ‘base&’ from an rvalue of type ‘int’
foo2(90);
^
para.cc:28:5: error: in passing argument 1 of ‘int foo2(base&)’
int foo2(base & x){
^
在剛剛的基礎(chǔ)上把foo2的參數(shù)加上const,<2>:
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
I[0x7fff2a5ac3f0] am init from <int>
I[0x7fff2a5ac400] am init from <int>
x - 0x7fff2a5ac400
I[0x7fff2a5ac400] am dying.
I[0x7fff2a5ac3f0] am dying.
這里沒(méi)有對(duì)復(fù)制構(gòu)造函數(shù)的調(diào)用.因?yàn)閰?shù)是引用類型. 在<3>處把復(fù)制構(gòu)造函數(shù)加上explicit聲明.編譯成功,并且結(jié)果與上面一樣.
附4:-fno-elide-constructors
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary which is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases.
大意就是強(qiáng)迫編譯器每次在產(chǎn)生臨時(shí)對(duì)象的時(shí)候,都通過(guò)復(fù)制構(gòu)造函數(shù)來(lái)構(gòu)造,測(cè)試一下,確實(shí)都明明白白出現(xiàn)了復(fù)制構(gòu)造函數(shù)的調(diào)用.
我先給出一段代碼.
#include <cstdio>
int seq;
class base{
public:
base();
~base();
base(int);
base(const base &);
base& operator=(const base &);
void value();
void add();
private:
int val;
};
inline base::base() : val(seq){
printf("I[%d,%p] am init from <default>\n", val, this);
seq++;
}
inline base::~base(){
printf("I[%d,%p] am dying.\n", val, this);
val = -1;
}
inline base::base(const base & b) : val(seq){
printf("I[%d,%p] am copied from [%d,%p]\n", val, this, b.val, &b);
seq++;
}
inline base::base(int k):val(seq){
printf("I[%d,%p] am init from <int>\n", val, this);
seq++;
}
base& base::operator=(const base &rhs){
printf("this:[%d,%p] | rhs:[%d,%p]\n", this->val, this,rhs.val , &rhs);
return *this;
}
void base::value(){
printf("[%d,%p]\n", val, this);
}
void base::add(){
val++;
}
base foo()
{
printf("===foo()===\n");
base x;
x.value();
return x;
}
base foo2(base x)
{
return x;
}
int main()
{
//base s = 100;//1 -->will do the follows: a. call base(int) to a tmp object; b. call base(const base &) to copy to s obj.
base test = foo();
printf("====in main()====\n");
test.value();
return 0;
}
這個(gè)在常規(guī)的編譯下結(jié)果是這樣:
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ g++ ret.cc -o test
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
===foo()===
I[0,0x7fff02e834d0] am init from <default>
[0,0x7fff02e834d0]
====in main()====
[0,0x7fff02e834d0]
I[0,0x7fff02e834d0] am dying.
觀察:完全沒(méi)有體現(xiàn)對(duì)復(fù)制構(gòu)造的調(diào)用,甚至main里的對(duì)象與foo里的對(duì)象是同一個(gè),我們很清楚已經(jīng)遇到了RVO了.編譯器把這部分優(yōu)化掉了,并且你也見(jiàn)不到復(fù)制構(gòu)造的調(diào)用.
同樣的代碼加上-fno-elide-constructors再來(lái)一次.
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ g++ ret.cc -o test -fno-elide-constructors
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
===foo()===
I[0,0x7fffc7200ad0] am init from <default>
[0,0x7fffc7200ad0]
I[1,0x7fffc7200b10] am copied from [0,0x7fffc7200ad0]
I[0,0x7fffc7200ad0] am dying.
I[2,0x7fffc7200b00] am copied from [1,0x7fffc7200b10]
I[1,0x7fffc7200b10] am dying.
====in main()====
[2,0x7fffc7200b00]
I[2,0x7fffc7200b00] am dying.
ok,代碼的運(yùn)行重新回到我們的三觀之內(nèi)了,可以看到,base test = foo();主要經(jīng)歷了:
- 進(jìn)入foo,聲明一個(gè)局部變量
0 - 到了foo要返回時(shí),一個(gè)臨時(shí)對(duì)象
1從0那復(fù)制,然后局部變量0析構(gòu). - 出foo,
test從臨時(shí)對(duì)象1那里復(fù)制,臨時(shí)對(duì)象1析構(gòu). - main結(jié)束前,
test對(duì)象析構(gòu).
所以一共涉及3個(gè)對(duì)象,而很具有迷惑性的RVO從頭到尾只有一個(gè)對(duì)象.
附5:對(duì)附4的擴(kuò)展
#include <cstdio>
int seq;
class base{
public:
base();
~base();
base(int);
base(const base &);
base& operator=(const base &);
void value();
void add();
private:
int val;
};
inline base::base() : val(seq){
printf("I[%d,%p] am init from <default>\n", val, this);
seq++;
}
inline base::~base(){
printf("I[%d,%p] am dying.\n", val, this);
val = -1;
}
inline base::base(const base & b) : val(seq){
printf("I[%d,%p] am copied from [%d,%p]\n", val, this, b.val, &b);
seq++;
}
inline base::base(int k):val(seq){
printf("I[%d,%p] am init from <int>\n", val, this);
seq++;
}
base& base::operator=(const base &rhs){
printf("this:[%d,%p] | rhs:[%d,%p]\n", this->val, this,rhs.val , &rhs);
return *this;
}
void base::value(){
printf("[%d,%p]\n", val, this);
}
void base::add(){
val++;
}
base foo()
{
printf("===foo()===\n");
base x;
x.value();
return x;
}
base foo2(base x)
{
printf("===foo2()===\n");
return x;
}
void test_foo()
{
seq = 0;
base test;
test = foo();
printf("====in test_foo()====\n");
test.value();
}
void test_foo2()
{
seq = 0;
base tt;
base test = foo2(tt);
printf("====in test_foo2()====\n");
test.value();
}
int main()
{
//base s = 100;//1 -->will do the follows: a. call base(int) to a tmp object; b. call base(const base &) to copy to s obj.
printf("=====start foo() test============\n");
test_foo();
printf("=====end foo() test============\n");
printf("\n");
printf("=====start foo2() test============\n");
test_foo2();
printf("=====end foo2() test============\n");
return 0;
}
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ g++ ret.cc -o test
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
=====start foo() test============
I[0,0x7fffeb2edb70] am init from <default>
===foo()===
I[1,0x7fffeb2edb80] am init from <default>
[1,0x7fffeb2edb80]
this:[0,0x7fffeb2edb70] | rhs:[1,0x7fffeb2edb80]
I[1,0x7fffeb2edb80] am dying.
====in test_foo()====
[0,0x7fffeb2edb70]
I[0,0x7fffeb2edb70] am dying.
=====end foo() test============
=====start foo2() test============
I[0,0x7fffeb2edb60] am init from <default>
I[1,0x7fffeb2edb80] am copied from [0,0x7fffeb2edb60]
===foo2()===
I[2,0x7fffeb2edb70] am copied from [1,0x7fffeb2edb80]
I[1,0x7fffeb2edb80] am dying.
====in test_foo2()====
[2,0x7fffeb2edb70]
I[2,0x7fffeb2edb70] am dying.
I[0,0x7fffeb2edb60] am dying.
=====end foo2() test============
test_foo();主要想與上面直接復(fù)制初始化對(duì)比,這里就不再?gòu)念^到尾只有一個(gè)對(duì)象了,而是一個(gè)外部自己的對(duì)象與一個(gè)賦值操作符.
test_foo2()主要是涉及參數(shù)的復(fù)制和一個(gè)臨時(shí)對(duì)象的復(fù)制.
下面演示的是關(guān)閉這些優(yōu)化的結(jié)果,很好,很清楚展示什么時(shí)候調(diào)用了復(fù)制,什么時(shí)候出現(xiàn)了臨時(shí)對(duì)象.
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ g++ ret.cc -o test -fno-elide-constructors
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
=====start foo() test============
I[0,0x7ffeef8c3ac0] am init from <default>
===foo()===
I[1,0x7ffeef8c3a90] am init from <default>
[1,0x7ffeef8c3a90]
I[2,0x7ffeef8c3ad0] am copied from [1,0x7ffeef8c3a90]
I[1,0x7ffeef8c3a90] am dying.
this:[0,0x7ffeef8c3ac0] | rhs:[2,0x7ffeef8c3ad0]
I[2,0x7ffeef8c3ad0] am dying.
====in test_foo()====
[0,0x7ffeef8c3ac0]
I[0,0x7ffeef8c3ac0] am dying.
=====end foo() test============
=====start foo2() test============
I[0,0x7ffeef8c3aa0] am init from <default>
I[1,0x7ffeef8c3ac0] am copied from [0,0x7ffeef8c3aa0]
===foo2()===
I[2,0x7ffeef8c3ad0] am copied from [1,0x7ffeef8c3ac0]
I[3,0x7ffeef8c3ab0] am copied from [2,0x7ffeef8c3ad0]
I[2,0x7ffeef8c3ad0] am dying.
I[1,0x7ffeef8c3ac0] am dying.
====in test_foo2()====
[3,0x7ffeef8c3ab0]
I[3,0x7ffeef8c3ab0] am dying.
I[0,0x7ffeef8c3aa0] am dying.
=====end foo2() test============
附6. 引用作為返回值
由于引用作為返回值與operator重載十分相關(guān),這部分移到另一篇《operator》專門(mén)討論.