`###### stage_0 基本
指針 - 變量。存儲的是一個地址
引用
- 是某個變量的別名项秉。引用和原變量在內(nèi)存的同一個區(qū)域绣溜。
<-----這是不是讓人想起了什么,軟鏈接硬鏈接區(qū)別是啥娄蔼? ---硬鏈接也是別名啊親
那引用本身有占據(jù)空間嗎? 這個我認(rèn)為應(yīng)該是要看編譯器的怖喻,標(biāo)準(zhǔn)里并沒有說要如何實現(xiàn)引用??,比如我可能自己實現(xiàn)一個符合標(biāo)準(zhǔn)的編譯器岁诉,其中的引用我就給他編譯成一個指針锚沸,那這樣你說占空間還是不占?但輸出引用的地址一定是和原始變量一致的.
- 引用類型必須初始化涕癣。否則報錯類似
error: ‘a(chǎn)_ref’ declared as reference but not initialized
- 有const指針哗蜈,沒有const引用;
int a(0);
int & const ref = a;//error: 'const' qualifier may not be applied to a reference
指針常量/常量指針
就是以
*
為分界坠韩,看const跟誰比較近就是修飾誰.
int *const ptr; //指針常量距潘,ptr本身不能被更改,*ptr可以被更改
const int *ptr1; //常量指針只搁,ptr本身可以更改音比,指向的內(nèi)容不能更改哦.
int const *ptr2; //同上,常量指針
- ref類型還不能重新被賦值须蜗。
原因是Bjarne(包括我也)覺得如果能重新綁定引用(或者叫重新指代)硅确,那么語法會變得很奇怪.就像胡蘿卜汁加咖啡一樣.
請check一下這個<a href=http://stackoverflow.com/questions/9293674/can-we-reassign-the-reference-in-c>thread</a>和effective c++37頁
//編譯通過,但不代表這就是重新指代
string k("dog1");
string k2("dog2");
string &c = k;
c = k2; //<--------------這句實際上的效果類似于 k = k2明肮,只是改變了值,并沒有改變c與k的綁定. 無法想象如何去重新綁定.
printf("%p %p %p\n", &c , &k, &k2); //實際上c依然引用k
- 不存在多級引用缭付,但有多級間接指針.
int a(100);
int* ptr = &a;
int** ptr_ptr = &ptr;
int &ref = a;
int &ref2 = ref;//這并不是引用的引用柿估,這還是a的引用.
//所以引用的語法我覺得就是在說明這問題,他是平坦的陷猫,只是個別名秫舌,并不存在間接關(guān)系.
printf("%p %p\n%p %p\n", ptr, *ptr_ptr, &ref, &ref2);
-
sizeof
操作符,sizeof引用對象可以得到原對象的大小绣檬,而sizeof指針只能得到指針大小.
<---------------又想起啥來了足陨!
//確定數(shù)組元素個數(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ù)組的引用
沒有引用數(shù)組,編譯不過娇未。
對數(shù)組的引用墨缘,需要注意,Type (&ref_name) [300] = Arr;
要加括號.
----------->又想起啥了.函數(shù)指針,指向數(shù)組的指針
也都是這樣镊讼,需要加括號宽涌,因為[]
的優(yōu)先級高于*
引用的自增與指針的自增(這應(yīng)該很容易推出的吧)
<a href=http://www.cnblogs.com/dolphin0520>參考</a>
stage_1 引用的一些其他
- 關(guān)于函數(shù)參數(shù)傳入方式 - 值/引用/地址
- 函數(shù)參數(shù)以值傳入時:
- 參數(shù)類型是與傳入的原始對象類型一致,則會引發(fā)復(fù)制構(gòu)造(隱式);
- 參數(shù)類型不一致蝶棋,且存在該類型的類型轉(zhuǎn)換構(gòu)造函數(shù)卸亮,則會:1. 通過該類型轉(zhuǎn)換構(gòu)造函數(shù)生成一個臨時對象,2. 通過復(fù)制構(gòu)造函數(shù)復(fù)制給參數(shù).但是運行時的表現(xiàn)是只調(diào)用了轉(zhuǎn)換構(gòu)造函數(shù). 因為
public復(fù)制構(gòu)造函數(shù)被優(yōu)化.(編譯器優(yōu)化玩裙,具體如何優(yōu)化的不得而知兼贸,有人說是public復(fù)制構(gòu)造函數(shù)被優(yōu)化,有人說是堆棧優(yōu)化)
1和2都是隱式調(diào)用吃溅,假如1/2中的構(gòu)造函數(shù)任意一者加了explicit
聲明寝受,編譯都會失敗.
(見附1) - 淺拷貝問題: 當(dāng)函數(shù)參數(shù)以值傳入時,會引發(fā)復(fù)制構(gòu)造罕偎,如果只是編譯器自動生成的,則只是對各個成員進行字面上的復(fù)制很澄,假如成員中有指針并且構(gòu)造/析構(gòu)會對申請釋放一段內(nèi)存,則很可能會出現(xiàn)二次釋放問題颜及。淺拷貝的解決方式:1. 使用引用傳遞甩苛,2.重寫復(fù)制構(gòu)造函數(shù),改為深拷貝.
(見附2)
- 函數(shù)參數(shù)以引用傳遞時:
- 參數(shù)類型一致: 不會引發(fā)構(gòu)造俏站,參數(shù)是原始對象的引用(一個別名).
- 參數(shù)類型不一致:1. 會生成一個臨時對象(隱式類型轉(zhuǎn)換讯蒲,可被explicit禁止)2. 參數(shù)類型必須帶有const修飾.(對臨時對象的引用)
(見附3)
- 關(guān)于函數(shù)返回值的方式 - 值/引用/地址(主要是對返回值的生命周期有些疑惑)
- 以值返回:情況與上面說的一樣,會引發(fā)復(fù)制構(gòu)造(?)肄扎,會有淺拷貝的問題.
- 原理上來講是需要一個臨時變量來存返回值:
- 從返回值說起(以局部對象返回):返回值是函數(shù)體內(nèi)部的變量墨林,在函數(shù)返回后已經(jīng)出作用域,需要析構(gòu).
- 臨時對象犯祠,在返回值析構(gòu)之前用一個臨時對象暫存該返回值(調(diào)用復(fù)制構(gòu)造)旭等,該臨時對象的生命周期在調(diào)用該函數(shù)的行后結(jié)束.
- 如果這行存在賦值或復(fù)制初始化等情況,則會調(diào)用復(fù)制構(gòu)造函數(shù)從臨時對象復(fù)制.
- 實現(xiàn)上則存在RVO(Return Value Optimization)的情形.編譯器優(yōu)化了1/2/3衡载,在調(diào)用行是初始化時直接把需要初始化的對象搞成了局部對象(該局部對象并沒有析構(gòu))搔耕,在調(diào)用行是賦值時直接從返回值復(fù)制. 省略了臨時對象.
(附4中詳細(xì)討論)
- 原理上來講是需要一個臨時變量來存返回值:
- 以引用返回:不會造成臨時對象的復(fù)制.但以引用返回實在很尷尬.
- 引用返回的方式一般是把返回值所在變量以引用參數(shù)傳遞進函數(shù),函數(shù)內(nèi)部對其進行修改后返回該引用.
- 在類的operator方法重載中經(jīng)常以*this方式返回一個引用.
(附6)
附:聲明性修飾——僅在函數(shù)聲明時寫該關(guān)鍵字即可痰娱,定義時不加.
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:對臨時對象的引用
#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;
}
沒做注釋處替換:
work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
I[0x7ffd5f840050] am init from <int>
x - 0x7ffd5f840050
I[0x7ffd5f840050] am dying.
替換注釋<1>所在行,其他地方不變. 原因是對臨時對象必須使用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.
這里沒有對復(fù)制構(gòu)造函數(shù)的調(diào)用.因為參數(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.
大意就是強迫編譯器每次在產(chǎn)生臨時對象的時候,都通過復(fù)制構(gòu)造函數(shù)來構(gòu)造鲸睛,測試一下,確實都明明白白出現(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;
}
這個在常規(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.
觀察:完全沒有體現(xiàn)對復(fù)制構(gòu)造的調(diào)用坡贺,甚至main里的對象與foo里的對象是同一個官辈,我們很清楚已經(jīng)遇到了RVO了.編譯器把這部分優(yōu)化掉了箱舞,并且你也見不到復(fù)制構(gòu)造的調(diào)用.
同樣的代碼加上-fno-elide-constructors
再來一次.
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,代碼的運行重新回到我們的三觀之內(nèi)了,可以看到钧萍,base test = foo();
主要經(jīng)歷了:
- 進入foo褐缠,聲明一個局部變量
0
- 到了foo要返回時,一個臨時對象
1
從0
那復(fù)制风瘦,然后局部變量0
析構(gòu). - 出foo队魏,
test
從臨時對象1
那里復(fù)制,臨時對象1
析構(gòu). - main結(jié)束前万搔,
test
對象析構(gòu).
所以一共涉及3個對象胡桨,而很具有迷惑性的RVO從頭到尾只有一個對象.
附5:對附4的擴展
#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ù)制初始化對比,這里就不再從頭到尾只有一個對象了瞬雹,而是一個外部自己的對象與一個賦值操作符.
test_foo2()主要是涉及參數(shù)的復(fù)制和一個臨時對象的復(fù)制.
下面演示的是關(guān)閉這些優(yōu)化的結(jié)果昧谊,很好,很清楚展示什么時候調(diào)用了復(fù)制酗捌,什么時候出現(xiàn)了臨時對象.
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》專門討論.