1. 程式人生 > >(轉載)C++的一些要點

(轉載)C++的一些要點

1.const 

示例程式碼:

#include <iostream>
int main(void)
{
 int x = 3;
 const int y = x;
 const int *p = &y;
 const int *c = &x;

 //error: l-value specifies const object
 //*p = 5;

 //error: cannot convert from 'const int *' to 'int *'
 //int *t = &y;

 //---------------------------------------------------
 int * const px = &x;

 //error: cannot convert from 'const int *' to 'int *const '
 //px = p;

 return 0;
}

const變數主要用於函式引數,使呼叫者不可改變傳入的指標指向的內容。

2. volatile

下面的說明來自msdn,告訴編譯器,這個變數可被其他程序修改,不要對其進行優化。

Objects declared as volatile are not used in optimizations because their value can change at any time. The system always reads the current value of a volatile object at the point it is requested, even if the previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.

One use of the volatile qualifier is to provide access to memory locations used by asynchronous processes such as interrupt handlers.

3.引用

變數的別名,對一個變數的引用的任何操作都是對本變數操作,必須初始化。

定義語法: 型別名 &引用名 = 被引用的變數;

引用是一個指標,但不用指標的語法。

引用必須指向一個變數;引用一旦指向一個變數,即不能指向其他的變數。

4.inline將把函式程式碼展開,同時作型別檢查,而巨集則不作型別檢查。

5.namespace

名稱空間是累計的,可跨越檔案。

給名稱空間起簡名: namespace 別名   = 名稱空間名;

using指示符: using namespace 名稱空間名;

示例程式碼:

#include <iostream>
using namespace std;

namespace n1
{
int x = 1;
int fun(int x)
{
   cout << "in namespace 1" << endl;
   return x + 1;
}
namespace n2
{
   int x = 2;
   int fun(int x)
   {
    cout << "in namespace 2" << endl;
    return x + 2;
   }
}
}

int x = 3;
int fun(int x)
{  
cout << "in namespace std";
return x + 3; 
}

int g(int x)
{
{
   using namespace n1::n2;
   int x = 4;
   return n1::n2::fun(x);
}
}

int main(void)
{
cout << fun(x) << endl;
cout << n1::fun(n1::x) << endl;
cout << n1::n2::fun(n1::x) << endl;
cout << n1::n2::fun(n1::n2::x) << endl;
cout << g(3);
return 0;
}


6.函式的過載

用一個名字定義多個引數,各個引數用形參個數和型別區分。

必須在同一個名稱空間中,不能用返回值區分。

示例程式:

#include <iostream>
using namespace std;

int abs(int x)
{
return x > 0 ? x : -x;
}

double abs(double x)
{
return x > 0 ? x : -x;
}

char abs(char x)
{
return x > 0 ? x : -x;
}

float abs(float x)
{
return x > 0 ? x : -x;
}

short abs(short x)
{
return x > 0 ? x : -x;
}

long abs(long x)
{
return x > 0 ? x : -x;
}

int main(void)
{
double a = 3.4;

//註釋掉double型別的abs函式後
//error: ambiguous call to overloaded function
cout << abs(a);

return 0;
}

<補充> 
1.名字空間 完全限定名(Fully Qualified Names),兩種解決方法: 
(1)使用typedef 把引用序列對映為一個較短的型別名以簡化書寫; 
(2)使用using宣告和using指令 
using宣告語法: 
using namespace::member; 
or 
using class::member; 
using指令語法: 
using namespace 名字空間名; 
名字空間一般用來分隔邏輯或業務上相互獨立的模組。 
2.實現範型程式設計的方法 
(1)使用帶引數的巨集; 
(2)使用void*,例如qsort(); 
(3)使用通用的根類和虛擬函式,比如java中的Object; 
(4)使用模板技術。 
模板最適合實現範型程式設計,它不僅直觀,而且是型別安全的,不會帶來任何額外的執行時開銷。 
template-->(例項化)Classes-->(例項化)Objects 
template-->(例項化)Functions 
如果設計的類的行為(介面)依賴於其所操作的資料型別,那麼選用模板,否則選用繼承和虛擬函式。 
3.模板例項化 
兩個模板類或兩個模板函式是型別等價的,當且僅當下列條件同時成立: 
(1)模板的名字相同; 
(2)型別實參完全相同(相容的型別為不同型別); 
(3)非型別實參的值完全相等; 
(4)當型別實參為另一種模板時,它們也必須相同。 
模板的例項化發生在第一次使用該模板時,而不是在其定義或編譯時。顯式例項化由關鍵字template或typename後跟帶模板實參的模板宣告(不是定義)或型別對映組成。 
4.模板的特化(specialization) 
就是針對某種特別的型別引數重新實現基本的模板,一般是出於對時空效率的優化或者滿足特殊的需要。例如:STL的vector<bool>。 
模板特化分為顯式特化(Explict Specialization)和部分特化(Particial Specialization)(vc不支援),在範型程式設計的traints技術中使用比較多。  

(1)連線指示符:通知編譯器,某個函式是由其它語言編寫的。

語法:

1. extern "語言名" 函式原型;

2. extern "語言名"

{

   函式原型1;

   ...

   函式原型n;

}

3.extern "語言名"

{

   include 檔名

}

原因:C++語言編譯器會對函式過載進行重新命名(Name-Mangling);但是在C語言中,由於不允許出現同名的全域性函式(靜態全域性函式除外),不需進行重新命名。所以在程式中如果出現了extern "C" fun(int);就不應出現extern "C" fun(double);

例如:

extern "C" fun(int);
extern "C" fun(double);
int main(void)
{
fun(3);
fun(3.5);
return 0;
}

error C2733: second C linkage of overloaded function 'fun' not allowed

(2)函式過載的解析

根據函式呼叫中的實參,確定要執行的函式。

結果:

1.沒有函式可呼叫(出錯);

2.恰有一個可呼叫;

3.有多個可呼叫,二義性(在編譯時發現)。

解析的時機:

1.函式呼叫時解析;

例如:

void fun(long);

void fun(int);

求:fun(3);

2.取函式地址。

例如:

void g(void (*fp)(long));

void g(void (*fp)(int));

求:g(f);

(3)實參型別轉換

1.精確匹配。包含4種情形(其中a,b.c稱為左值轉換):

a.從左值到右值的轉換

b.從陣列到指標的轉換

c.從函式到指標的轉換

例如: int fun(int);

void g(int (*fp)(int));

求:g(fun);

d.限定修飾符轉換

例如:int *q;

void fun(const int * p);

求:fun(q);

2.提升

a.帶符號或不帶符號的char,short型別轉換為int型。

b.float   => double

c.bool   => int

3.標準轉換

a.算術型別間的轉換,這與提升a,b是一樣的。

b.整型0到各種指標;各種指標 => void *

c.各種算術指標 => bool

4.使用者自定義轉換

標準轉換序列:

0個或1個左值轉換 -> 0個或1個提升或標準轉換 -> 0個或1個限定修飾符轉換

過載函式解析的過程:

1.確定候選函式集

a.呼叫點可見的同名函式

b.實參型別定義所屬名字空間中的同名函式

2.確定可行函式集

a.實參個數 <= 形參個數,形參多出的必有預設值。

b.有實參到形參的轉換序列。

3.確定最佳可行函式。

(4)函式模板

1.功能不同或者是功能太大時不要使用函式過載。

所謂函式過載,是指自動生成各種型別函式的演算法。

定義語法:template <模板引數表>

                    值型別 函式名 (形參表)

                    函式體

模板引數,由兩類引數構成:

a.模板型別引數

宣告方式: typename 識別符號

表示一個特定型別。

b.模板非型別引數

宣告方式: 通常的變數宣告

表示一個值。

說明:

值型別,可以是內建型別,使用者定義型別,模板型別引數

形參表,允許型別如上

函式表,定義的區域性變數型別如上。

例如:

#include <iostream>
using namespace std;

template <typename T, int size>
T min(T (&a)[size])
{
int i;
T x = a[0];
for(i = 0; i < size; i++)
{
   if (x > a[i])
    x = a[i];
}
return x;
}

template <typename T>
T min(T *a, int size)
{
int i;
T x = a[0];
for(i = 0; i < size; i++)
{
   if (x > a[i])
    x = a[i];
}
return x;
}

int main()
{
int a[] = {1, 2, 3, -1, 5, 2, 4};
cout << min(a) << endl;
cout << min(a, 7) << endl;
}

模板例項化:

根據函式呼叫,自動生成函式。

說明:

顯示指定模板引數語法:

函式名<實參名, ..., 實參名>(實參表);

例如:

template <typename T1, typename T2, typename T3>

T1   fun(T2 x, T3 y)

{

   ...

}

fun<int, int, double>(3, 2.5);

函式實參推演:從函式呼叫的實參確定模板引數的過程。

模板特化語法:

template <>

值型別 函式名<實際模板引數值>(形參表)

函式體

舉例略。

函式模板的過載:

1.求候選函式集,較為複雜,略。

2.求可行函式集

3.最佳可行函式集

在寫函式模板時,可以先寫好特殊型別下的,再寫一般的。例如:排序函式模板程式如下。

#include <iostream>
using namespace std;

template <typename T>
void sort(T *array, int n)
{
cout << "sort in template 1..." << endl;

//下標
int i, j;

//暫存待排序元素
T tmp;

for(i = 1; i < n; i++)
{
   tmp = array[i];
   j = i - 1;
   //尋找插入位置
   while(j >= 0 && array[j] > tmp)
   {
    array[j + 1] = array[j];
    j--;
   }
   array[j + 1] = tmp;
}
}

template <typename T1, typename T2>
void sort(T1 *array, int n, T2 fun)
{
cout << "sort in template 2..." << endl;

//下標
int i, j;

//暫存待排序元素
T1 tmp;

for(i = 1; i < n; i++)
{
   tmp = array[i];
   j = i - 1;
   //尋找插入位置
   while(j >= 0 && fun(array[j], tmp))
   {
    array[j + 1] = array[j];
    j--;
   }
   array[j + 1] = tmp;
}
}

int cmp(int a, int b)
{
return a > b;
}

int main()
{
int a[] = {1, 3, 5, 8, 9, 4, 6, 7, 2};
int size = sizeof(a) / sizeof(int);
sort(a, size);
for(int i = 0; i < size; i ++)
{
   cout << a[i] << endl;
}
cout << endl;

sort(a, size, cmp);
for(int i = 0; i < size; i ++)
{
   cout << a[i] << endl;
}
cout << endl;

double b[] = {1.0, 3.0, 5.0, 8.0, 9.0, 4.0, 6.0, 7.0, 2.0};
int dsize = sizeof(b) / sizeof(double);
sort(b, dsize);
for(int j = 0; j < dsize; j++)
{
   cout << b[j] << endl;
}
cout << endl;

sort(b, dsize, cmp);
for(int j = 0; j < dsize; j++)
{
   cout << b[j] << endl;
}

}

<補充> 
1.確定候選函式集

a.呼叫點可見的同名函式

b.實參型別定義所屬名字空間中的同名函式

對於b項,老師舉例:

namespace N 

class A{}; 
void f(A x){}; 
void f(int){}; 

void f(double){}

int main() 

N::A x; 
f(x); 
}