C語言到C++的OOP 面向對象編程

鯨90830 C語言入門到精通 2020-12-21html

圖片

連接:https://www.cnblogs.com/whale90830/p/10488595.htmlios

由C到C++

OOP第一課程序員


  • C語言的侷限編程

  • C++的特色數組

  • C++的程序特徵安全

  • C++程序的結構特性ide

  • C++程序的編輯、編譯和運行函數

  • ⭐C++對C的補充測試

C語言的侷限

  • 類型檢查機制相對較弱,使得程序中的一些錯誤不能在編譯時由編譯器檢查出來。優化

  • C語言自己沒有支持代碼重用的語言結構

  • 不適合開發大型程序,當程序的規模達到必定的程度時,程序員很難控制程序的複雜性。

C++的特色

C++繼承了C的優勢,並有本身的特色,主要有:


一、全面兼容C,C的許多代碼不經修改就能夠爲Cpp所用,用C編寫的庫函數和實用軟件能夠用於Cpp。

二、用C++編寫的程序可讀性更好,代碼結構更爲合理,可直接在程序中映射問題空間結構。

三、生成代碼的質量高,運行效率高。

四、從開發時間、費用到造成軟件的可重用性、可擴充性、可維護性和可靠性等方面有了很大提升,使得大中型的程序開發項目變得容易得多。

五、支持面向對象的機制,可方便的構造出模擬現實問題的實體和操做。

C++的程序特徵

例1.1 輸出一行字符:「This is a C++ program.」。


程序以下:


#include <iostream> //包含頭文件iostream
using namespace std//使用命名空間std
int main( )
{
    cout<<″This is a C++ program.″;
    return 0;
}


在運行時會在屏幕上輸出如下一行信息:


This is a C++ program.


  • 用main表明「主函數」的名字。每個C++程序都必須有一個 main 函數。main前面的int的做用是聲明函數的類型爲整型。程序第6行的做用是向操做系統返回一個零值。若是程序不能正常執行,則會自動向操做系統返回一個非零值,通常爲-1。

  • 函數體是由大括號{}括起來的。本例中主函數內只有一個以cout開頭的語句。注意C++全部語句最後都應當有一個分號。

  • 再看程序的第1行「#include

  • 」,這不是Cpp的語句,而是Cpp的一個預處理命令,它以「#」開頭以與Cpp語句相區別,行的末尾沒有分號。

  • #include <iostream>是一個「包含命令」,它的做用是將文件iostream的內容包含到該命令所在的程序文件中,代替該命令行。文件iostream的做用是向程序提供輸入或輸出時所須要的一些信息。

  • iostream是i-o-stream3個詞的組合,從它的形式就能夠知道它表明「輸入輸出流」的意思,因爲這類文件都放在程序單元的開頭,因此稱爲「頭文件」 (head file)。在程序進行編譯時,先對全部的預處理命令進行處理,將頭文件的具體內容代替#include命令行,而後再對該程序單元進行總體編譯。

  • 程序的第2行「using namespace std;」的意思是「使用命名空間std」。Cpp標準庫中的類和函數是在命名空間std中聲明的,所以程序中若是須要用到Cpp標準庫(此時就須要用#include命令行),就須要用「using namespace std;」做聲明,表示要用到命名空間std中的內容。

  • 在初學C++時,對本程序中的第1,2行能夠沒必要深究,只需知道:若是程序有輸入或輸出時,必須使用「#include

  • 」命令以提供必要的信息,同時要用「using namespace std;」,使程序可以使用這些信息,不然程序編譯時將出錯。



例1.2 求a和b兩個數之和


// 求兩數之和 (本行是註釋行)
#include <iostream> //預處理命令
using namespace std//使用命名空間std
int main( ) //主函數首部
//函數體開始
    int a,b,sum; //定義變量
    cin>>a>>b; //輸入語句
    sum=a+b; //賦值語句
    cout<<″a+b=″<<sum<<endl//輸出語句
    return 0//如程序正常結束,向操做系統返回一個零值
//函數結束


本程序的做用是求兩個整數a和b之和sum。


第1行「//求兩數之和」是一個註釋行,Cpp規定在一行中若是出現「//」 ,則從它開始到本行末尾之間的所有內容都做爲註釋。


例1.3 給兩個數x和y, 求兩數中的大者


#include <iostream> //預處理命令
using namespace std;
int max(int x,int y) //定義max函數,函數值爲整型,形式參數x, y爲整型
//max函數體開始
    int z; //變量聲明,定義本函數中用到的變量z爲整型
    if(x>y) z=x; //if語句,若是x>y, 則將x的值賦給z
    else z=y; //不然,將y的值賦給z
    return(z); //將z的值返回,經過max帶回調用處
//max函數結束
int main( ) //主函數
//主函數體開始
    int a,b,m; //變量聲明
    cin>>a>>b; //輸入變量a和b的值
    m=max(a,b); //調用max函數,將獲得的值賦給m
    cout<<″max=″<<m<<′\\n′; //輸出大數m的值
    return 0//如程序正常結束,向操做系統返回一個零值
//主函數結束


本程序包括兩個函數:主函數main和被調用的函數max。


程序運行狀況以下:


  • 18 25 ↙ (輸入18和25給a和b)

  • max=25 (輸出m的值)


注意輸入的兩個數據間用一個或多個空格間隔,不能以逗號或其餘符號間隔。


在上面的程序中,max函數出如今main函數以前,所以在main函數中調用max函數時,編譯系統能識別max是已定義的函數名。若是把兩個函數的位置對換一下,即先寫main函數,後寫max函數,這時在編譯main函數遇到max時,編譯系統沒法知道max表明什麼含義,於是沒法編譯,按出錯處理。


爲了解決這個問題,在主函數中須要對被調用函數做聲明。上面的程序能夠改寫以下:


#include <iostream>
using namespace std;
int max(int x,int y)//對max函數做聲明
int main( )
{
    int a,b,c;
    cin>>a>>b;
    c=max(a,b); //調用max函數例1.3 給兩個數x和y, 求兩數中的大者。
    cout<<″max=″<<c<<endl;
    return 0;
}
int max(int x,int y) //定義max函數
{
    int z;
    if(x>y) z=x;
    else z=y;
    return(z);
}


只要在被調用函數的首部的末尾加一個分號,就成爲對該函數的函數聲明。函數聲明的位置應當在函數調用以前。

C++程序的結構特性

一個面向對象的C++程序通常由類的聲明和類的使用兩大部分組成。


類的使用部分通常由主函數及有關子函數組成。


典型的C++程序結構


#include <iostream.h>
//類的聲明部分
class A{
    int x,y,z;
    ……
    fun( ){……}
    ……
};
//類的使用部分
int main()
{
    A a;
    ……
    a.fun();
    return 0;
}


在C++程序中,程序設計始終圍繞「類」展開。經過聲明類,構建了程序所要完成的功能,體現了面向對象程序設計的思想。

C++程序的編輯、編譯和運行

C++源程序文件的擴展名爲.CPP


能夠用多種編譯器編輯、編譯和運行


C++對C的補充


一、註釋與續行


  • 註釋符:「/*」和「*/」 或「//」 。


Cpp新增了註釋語句,它由「//」開始,到行尾結束。


例如:


X = y + z; /*This is a comment */

X = y + z; //This is a comment


  • 續行符:「\」(反斜槓)。做用是當一個語句太長時能夠用該符號把它分段寫在幾行中。
    例:


cout << ‘\n’ << 「x=」 << x << 「y=」 << y << 「z=」 << z\
 << 「u=」 << u << 「v\
=」 << v << 「w=」 << w << endl;

二、輸入輸出流

C中I/O操做出現的問題:


int i;
float f;
scanf(「%f」,i);
printf( 「%d」,d);


Cpp中使用更安全更方便的方法:


int i;
float f;
cin >> i;
cout << f;


cout和cin分別是C++的標準輸出流和輸入流。Cpp支持重定向,但通常cout指的是屏幕, cin指的是鍵盤。


操做符「<<」和「>>」除了具備C語言中定義的左移和右移的功能外,在這裏符號「<<」是把右方的參數寫到標準輸出流cout中;相反,符號「>>」則是將標準輸入流的數據賦給右方的變量。


例1.4 一個完整的C++程序


#include <iostream.h>
int main()
{
    char name[20];
    cout << "Hello, your name: ";
    cin >> name;
    cout << name;
    return 0;
}


注意:

  • 程序中必須包含頭文件iostream.h

  • cin和>>,cout和<<配套使用

  • cin能夠輸入多個數據,但要用空白符隔開(tab,空格,回車)

如:cin >> a >> b >> c;

  • 換行符:‘\n’或endl

如:cout << 「x=」 << x << endl; cout << 「x=」 << x << ‘\n’;

  • 使用cout和cin時,也能夠對輸入和輸出的格式進行控制,好比可用不一樣的進制方式顯示數據,只要設置轉換基數的操做符dec、hex和oct便可。


例1.5 操做符dec、 hex和oct的使用


#include<iostream.h>
void main()
{
    int x=25;
    cout << hex << x << ' ' << dec << x << ' ' << oct << x << '\n';
}


輸出結果爲:19 25 31


三、靈活的變量說明


定義變量的位置


在程序中的不一樣位置採用不一樣的變量定義方式,決定了該變量具備不一樣的特色。變量的定義通常可有如下三種位置:


(1) 在函數體內部


在函數體內部定義的變量稱爲局部變量,這種局部變量只在進入定義它的函數體時起做用,離開該函數體後該變量就消失(被釋放),即再也不起做用。所以,不一樣函數體內部能夠定義相同名稱的變量,而互不干擾。


(2) 形式參數


當定義一個有參函數時,函數名後面括號內的變量,統稱爲形式參數。


(3) 全局變量


在全部函數體外部定義的變量,其做用範圍是整個程序,並在整個程序運行期間有效。


  • 在C語言中,全局變量聲明必須在任何函數以前,局部變量必須集中在可執行語句以前。

  • Cpp中的變量聲明很是靈活,它容許變量聲明與可執行語句在程序中交替出現。


例如


f( )
{
    int i;
    i=10;
    int j;
    j=25;
    // …
}
float fun(int x,int y)
{
    for(int i=0;i<10;i++)
    {
        int sum=0;
        sum=sum+i;
        cout<<「sum=」<<sum;
    }
    int z=0;
    z=x+y;
}

四、結構、聯合和枚舉名

在C++中,結構名、聯合名、枚舉名都是類型名。在定義變量時,沒必要在結構名、聯合名或枚舉名前冠以struct、union或enum。


例如:


enum boole{FALSE,TRUE};
struct string{
    char *string;
    int length;
};
union number{
    int i;
    float f;
};


在傳統的C中,定義變量時,必須寫成:


enum boole done;
struct string str;
union number x;


可是,在C++中,能夠說明爲:


boole done;
string str;
number x;

五、函數原型

C語言建議編程者爲程序中的每個函數創建原型,而Cpp要求爲每個函數創建原型,以說明函數的名稱、參數類型與個數,以及函數返回值的類型。


其主要目的是讓C++編譯程序進行類型檢查,即形參與實參的類型匹配檢查,以及返回值是否與原型相符,以維護程序的正確性。


例如


int sum(int a,int b);   //是函數sum的原型


  • 函數原型語法的通常形式爲:返回類型 函數名(參數表);

  • 函數原型是一條語句,它必須以分號結束。


例1.6 函數原型的說明


#include<iostream.h>
void write(char *s);
void main()
{write("Hello,world!");}
void write(char *s)
{cout<<s;}


在程序中,要求一個函數的原型出如今該函數的調用語句以前。


說明:


  • 函數原型的參數表中可不包含參數的名字,而只包含它們的類型。例如:long Area(int ,int);

  • 函數定義由函數首部和函數體構成。函數首部和函數原型基本同樣,但函數首部中的參數必須給出名字並且不包含結尾的分號。

  • Cpp的參數說明必須放在函數說明後的括號內,不可將函數參數說明放在函數首部和函數體之間。這種方法只在C中成立。

  • 主函數沒必要進行原型說明,由於它被當作自動說明原型的函數。

  • 原型說明中沒有指定返回類型的函數(包括主函數main),Cpp默認該函數的返回類型是int

  • 若是一個函數沒有返回值,則必須在函數原型中註明返回類型爲void,主函數相似處理。

  • 若是函數原型中未註明參數,Cpp假定該函數的參數表爲空(void)。


六、const修飾符


  • 在C中,習慣使用#define定義常量。


通常格式: #define 宏名 常數



#define PI 3.14
…………
s = 2 * PI * r;
…………


  • C++利用const定義正規常數


通常格式:const 數據類型標識符 常數名=常量值;


採用這種方式定義的常量是類型化的,它有地址,能夠用指針指向這個值,但不能修改它。

說明:


一、const必須放在被修飾類型符和類型名前面

二、數據類型是一個可選項,用來指定常數值的數據類型,若是省略了該數據類型,那麼編譯程序認爲它是 int 類型。

如:const int a=10; 表示定義了一個初始值爲10的整型常量,它在程序中不可改變,但可用於表達式的計算中。

例2.6 #define的不安全性


#include "iostream.h"
main()
{
    int a=1;
    #define T1 a+a
    #define T2 T1-T1
    cout<<"T2 is "<<T2<<endl;
    return 0;
}


但實際的輸出是:T2 is 2

const做用與#define類似,但消除了#define的不安全性。

若是用const取代了兩個#define,就不會引發這個錯誤。


#include<iostream.h>
int main()
{
    int a=1;
    const T1=a+a;
    const T2=T1-T1;
    cout <<"T2 is"<<T2<<endl;
    return 0;
}

const能夠與指針一塊兒使用

  • (1)指向常量的指針:一個指向常量的指針變量。


例如:


const char* pc=「abcd」;  //聲明指向常量的指針
pc[3]=‘x’;  //錯誤

pc=「efgh」;  //容許


  • (2)常指針:把指針自己,而不是它指向的對象聲明爲常量。


例如:


charconst pc=「abcd」;  //常指針
pc[3]=‘x’;  //合法
pc=「efgh」;  //出錯


建立一個常指針,就是建立一個不能移動的固定指針,可是它所指的數據能夠改變。例如:


  • (3)指向常量的常指針:這個指針自己不能改變,它所指向的值也不能改變。


要聲明一個指向常量的常指針,兩者都要聲明爲const。


例如:


const charconst pc=「abcd」;    //指向常量的常指針
pc[3]=‘x’;  //出錯
pc=「efgh」;  //出錯


這個語句的含義是:聲明瞭一個名爲pc的指針變量,它是一個指向字符型常量的常指針,用「abcd」的地址初始化該指針。


說明


  • (1). 若是用const定義的是一個整型常量,關鍵詞int能夠省略。因此下面的兩語句是等價的

    const int bufsize=200;

    const bufsize=200;

  • (2). 常量一旦被創建,在程序的任何地方都不能再更改。

  • (3). 與#define定義的常量有所不一樣,const定義的常量能夠有本身的數據類型,這樣C++的編譯程序能夠進行更加嚴格的類型檢查,具備良好的編譯時的檢測性。

  • (4). 函數參數也能夠用const說明,用於保證明參在該函數內部不被改動,大多數C++編譯器能對具備const參數的函數進行更好的代碼優化。

例如:經過函數i_Max求出整型數組a[200]中的最大值,函數原型應該是:int i_Max(const int* ptr);

這樣作的目的是確保原數組的數據不被破壞,即在函數中對數組元素的操做只許讀,而不準寫。調用時的格式能夠是:i_Max(a);

七、void型指針

void 一般表示無值,但將void做爲指針的類型時,它卻表示不肯定的類型。


這種void型指針是一種通用型指針,也就是說任何類型的指針值均可以賦給void類型的指針變量。


例以下面的程序段


void pa;    //錯誤,不能聲明void類型的指針變量
void* pc;   //正確,能夠聲明void類型的指針
int i=456;
char c=‘a’;
pc=&i;
pc=&c;


void型指針能夠接受任何類型的指針的賦值,但對已獲值的void型指針,對它在進行處理,如輸出或傳遞指針值時,則必須進行強制類型轉換,不然會出錯。


#include <iostream.h>
main()
{
    void *pc;
    int i=456;
    char c='a';
    pc=&i;
    cout<<*(int *)pc<<endl;
    pc=&c;
    cout<<*(char *)pc<<endl;
    return 0;

八、內聯函數

調用函數時系統要付出必定的開銷,用於信息入棧出棧和參數傳遞等。特別是對於那些函數體較小但調用又較頻繁的函數,計算機的開銷相對就比較可觀。


在C語言中,用宏替換,可解決這個問題。例如,有以下的函數:


add(int x,int y)
{
    return x+y;
}


用宏替換時,上面的函數功能可寫爲:


#define add(x,y) x+y


C++引進了內聯函數(inline function)的概念。


宏替換實質上是文字替換。內聯函數與通常函數不一樣的是,在進行程序的編譯時,編譯器將內聯函數的目標代碼做拷貝並將其插入到調用內聯函數的地方。


例1.7 內聯函數的使用


#include "iostream.h"
inline double circle(double r)
{return 3.1416*r*r;}
int main()
{
    for(int i=1;i<=3;i++)
        cout<<"r="<<i<<" area="<<circle(i)<<endl;
    return 0;
}

說明:


  • (1). 內聯函數在第一次被調用前必須進行聲明或定義,不然編譯器將沒法知道應該插入什麼代碼。

  • (2). C++的內聯函數具備與C中的宏定義#define相同的做用和相似機理,但消除了#define的不安全性。

  • (3). 內聯函數體內通常不能有循環語句和開關語句。

  • (4). 後面類結構中全部在類說明體內定義的函數都是內聯函數。

  • (5). 一般較短的函數才定義爲內聯函數。


九、帶有缺省參數值的函數


在C++中,函數的參數能夠有缺省值。


當調用有缺省參數的函數時,若是相應的參數沒有給出實參,則自動用相應的缺省參數值做爲其實參。


函數的缺省參數,是在函數原型中給定的。


例如:


int init(int x=5int y=10);
init(100,80);   //容許
init(25);   //容許
init();   //容許

說明:

  • (1)在函數原型中,全部取缺省值的參數必須出如今不取缺省值的參數的右邊。

    int fun(int I,int j=5,int k); 錯誤

    int fun(int I,int k,int j=5); 正確

  • (2)在函數調用時,若某個參數省略,則其後的參數皆應省略而採用缺省值。

    init (,20) 錯誤


例.編寫一個帶有默認參數的函數,使得在默認狀況下顯示兩個整數的較大者,不然顯示兩個整數的較小者。
 
 

int main()
{
    void showValue(int xint y, bool Max = true); // 聲明函數
    int a = 5, b = 10;
    showValue(a,b);
    showValue(a,b,false);
    return 0;
}
void showValue(int xint y, bool Max = true) // 定義函數
{
    if(Max)
        cout << 「the bigger value is: " << (x>y)?x:y << endl;
    else
        cout << "
the smaller value is: " << (x<y)?x:y << endl;
}

十、函數重載

(1) 什麼是函數重載


函數重載是指一個函數能夠和同一做用域中的其餘函數具備相同的名字,但這些同名函數的參數類型、參數個數不一樣。如:


#include <iostream.h>
void whatitis(int i)
cout<<"this is integer"<<i<<endl;}
void whatitis(char c[])
cout<<「this is string」<<c<<endl; }
main()
{
    int i=1;
    char c[]="abcdef";
    whatitis(i);
    whatitis(c);
}


在本例中定義了兩個名稱都叫whatitis的函數,但它們的形參類型不一樣。所以,這兩個函數就是重載函數。


(2) 爲何要使用函數重載


在原有C語言中,每一個函數必須有其惟一的名稱,這樣的缺點是全部具備相同功能、而只是函數參數不同的函數,就必須用一個不一樣的名稱.


而C++中採用了函數重載後,對於具備同一功能的函數,若是隻是因爲函數參數類型不同,則能夠定義相同名稱的函數。


(3) 匹配重載函數的順序


因爲重載函數具備相同的函數名,在進行函數調用時,系統通常按照調用函數時的參數個數、類型和順序來肯定被調用的函數。


具體來講,按如下三個步驟的前後次序找到並調用那個函數:


  • (1)尋找一個嚴格的匹配,即:調用與實參的數據類型、個數徹底相同的那個函數。

  • (2)經過內部轉換尋求一個匹配,即:經過(1)的方法沒有找到相匹配的函數時,則由C++系統對實參的數據類型進行內部轉換,轉換完畢後,若是有匹配的函數存在,則執行該函數。

  • (3)經過用戶定義的轉換尋求一個匹配,若能查出有惟一的一組轉換,就調用那個函數。即:在函數調用處由程序員對實參進行強制類型轉換,以此做爲查找相匹配的函數的依據。


例1.8 重載例子


#include <iostream.h>
void print(double d)
cout<<"this is a double "<<d<<"\n"; }
void print(int i)
cout<<"this is an integer "<<i<<"\n"; }
void main()
{
    int x=1,z=10;
    float y=1.0;
    char c='a';
    print(x);   //按規則(1)自動匹配函數void print(int i)
    print(y);   //按規則(2)經過內部轉換匹配函數 void print(double i)
    //由於系統能自動將float型轉換成double型
    print(c);   //按規則(2)經過內部轉換匹配函數 void print(int i)
    //由於系統能自動將char型轉換成int型
    print(double(z));   //按規則(3)匹配void print(double i)
    //由於程序中將實參z強制轉換爲double型。
}
例 重載例子

編寫一個程序,用來求兩個整數或3個整數中的最大數。若是輸入兩個整數,程序就輸出這兩個整數中的最大數,若是輸入3個整數,程序就輸出這3個整數中的最大數。


#include <iostream>
using namespace std;
int main( )
{
    int max(int a,int b,int c)//函數聲明
    int max(int a,int b)//函數聲明
    int a=8, b=-12, c=27;
    cout<<"max(a,b,c)="<<max(a,b,c)<<endl//3個整數中的最大者
    cout<<"max(a,b)="<<max(a,b)<<endl//2個整數中的最大者
}
int max(int a,int b,int c)
{
    if(b>a) a=b;
    if(c>a) a=c;
    return a;
}
int max(int a,int b)
{
    if(a>b) return a;
    else return b;
}

(4) 定義重載函數時的注意事項

  • 重載函數間不能只是函數的返回值不一樣,應至少在形參的個數、參數類型或參數順序上有所不一樣。
    如:


void myfun(int i)
{………………}
int myfun(int i)
{………………}
// 這種重載是錯誤的

  • 應使全部的重載函數的功能相同。若是讓重載函數完成不一樣的功能,會破壞程序的可讀性。


(5) 函數模板


1) 函數模板 (function template):

創建一個通用函數,其函數類型和形參類型不具體指定,而是一個虛擬類型。


2) 應用狀況:

凡是函數體相同的函數均可以用這個模板來代替,沒必要定義多個函數,只需在模板中定義一次便可。在調用函數時系統會根據實參的類型來取代模板中的虛擬類型,從而實現了不一樣函數的功能。


3) 通常形式:
- template < typename T> // 模板頭 通用函數定義
- template <class T> // 模板頭 通用函數定義
- template <class T1,typename T2> // 多個參數 通用函數定義

說明:class與typename能夠通用


#include <iostream>
using namespace std;
template<typename T> // 模板聲明,其中T爲類型參數
max(T a, T b) // 定義一個通用函數, T做爲虛擬的類型名
{
    if(b>a) return b;
    else return a;
}
//template <typename T> T max(T a, T b)
//{
//…
//}
int main( )
{
    int i1=111, i2=222, i;
    double d1=12.34, d2=56.78,d;
    i=max(i1,i2); // 調用模板函數,此時T被 int 取代
    d=max(d1,d2,d3); // 調用模板函數,此時T被 double 取代
    cout<<"i_max=" << i <<endl;
    cout<<"f_max=" <<f<<endl;
    return 0;
}
函數模板說明:

1) 在對程序進行編譯時,遇到第13行調用函數max(i1,i2), 編譯系統會將函數名max與模板max相匹配,將實參的類型取代了函數模板中的虛擬類型T。此時至關於已定義了一個函數,而後調用它。


int max(int a,int b)
{
if(b>a) a=b;
if(c>a) a=c;
return a;
}


2) 與重載函數比較:用函數模板比函數重載更方便,程序更簡潔。但應注意它只適用於:函數的參數個數相同而類型不一樣,且函數體相同的狀況。若是參數的個數不一樣,則不能用函數模板;


3) main函數不能定義爲模板函數。


11. 做用域標示符::


一般狀況下,若是有兩個同名變量,一個是全局的,另外一個是局部的,那麼局部變量在其做用域內具備較高的優先權。


下面的例子說明了這個問題。


#include "iostream.h"
int avar=10;
main( )
{
    int avar;
    avar=25;
    cout<<"avar is"<<avar<<endl;
    return 0;
}


若是但願在局部變量的做用域內使用同名的全局變量,能夠在全局變量加上「::」,此時::avar表明全局變量avar


#include <iostream.h>
int avar=10;
main()
{
int avar;
avar=25;
cout<<"local avar ="<<avar<<endl;
cout<<"global avar="<<::avar<<endl;
return 0;
}

十二、無名聯合

無名聯合是C++中的一種特殊聯合,能夠聲明一組無標記名共享同一段內存地址的數據項。如:


union{
    int i;
    float f;
}


在此無名聯合中,聲明瞭變量i和f具備相同的存儲地址。無名聯合可經過使用其中數據項名字直接存取,例如能夠直接使用上面的變量i或f,如:i=20;


1三、強制類型轉換


在C中數據類型轉換的通常形式:(數據類型標識符)表達式


int i=10;
float x=(float) i;


C++支持這樣的格式,還提供了一種更爲方便的函數調用方法,即將類型名做爲函數名使用,使得類型轉換的執行看起來好像調用了一個函數。形式爲:數據類型標識符 (表達式)


int i=10;
float x=float(i);


以上兩種方法C++都接受,但推薦使用後一種方式。


1四、動態內存分配


做爲對C語言中malloc和free的替換,C++引進了new和delete操做符。它們的功能是實現內存的動態分配和釋放。


動態分配new的通常形式是:


  • 指針變量=new 數據類型;

  • 指針變量=new 數據類型(初始值);


int *a, *b;
a=new int;
b=new int(10);


釋放由new操做動態分配的內存時,用delete操做。


它的通常形式是:delete 指針變量;


delete a;
delete b;


例1.9 操做符new和delete的使用


#include <iostream.h>
main()
{
    int *p; // 聲明一個整型指針變量p
    p=new int// 動態分配一個存放int型數據的內存空間,並將首地址賦給p
    *p=10;
    cout<<*p;
    delete p; // 釋放指針變量p指向的內存空間
    return 0;
}
例1.10 將new和delete用於結構類型


#include<iostream.h>
#include<string.h>
struct person {
    char name[20];
    int age;
};
main()
{
    person *p;
    p=new person;
    strcpy(p->name, "Wang Fun");
    p->age=23;
    cout<<"\n"<<p->name<<" "<<endl<<p->age;
    delete p;
    return 0;
}


與C的內存動態分配和釋放操做(malloc和free)相比,C++提供的動態分配有如下優勢


  • (1) new和delete 操做自動計算須要分配和釋放類型的長度。這不但省去了用sizeof計算長度的步驟,更主要的是避免
    了內存分配和釋放時因長度出錯帶來的嚴重後果;

  • (2) new操做自動返回需分配類型的指針, 無需使用強制類型轉換;

  • (3) new操做能初始化所分配的類型變量。

  • (4) new和delete都能能夠被重載,容許創建自定義的內存管理法。


對使用new和delete的幾點說明:


  • (1)用new分配的空間,使用結束後應該用delete顯示的釋放,不然這部分空間將不能回收而變成死空間。

  • (2)使用new動態分配內存時,若是沒有足夠的內存知足分配要求, new將返回空指針(NULL)。所以一般要對內存的動態分配是否成功進行檢查。


例1.11 對內存的動態分配是否成功進行檢查


#include <iostream.h>
main()
{
    int * p;
    p=new int;
    if(!p){
        cout<<"allocation failure\n";
        return 1;
    }
    *p=20;
    cout<<*p;
    delete p;
    return 0;
}


  • (3) 使用new能夠爲數組動態分配內存空間這是須要在類型後面綴上數組大小。其語法形式爲:
    指針變量=new 類型名 [下標表達式];

    如:int *pi=new int[2][3][4];

    其中第一維的界值能夠是任何合法的表達式,如:int i=3; int *pi=new int[ i ][2][3];

    例如:int *pi=new int[10];

    這時new爲具備10個元素的整型數組分配了內存空間,並將首地址賦給了指針pi。

    使用new爲多維數組分配空間時,必須提供全部維的大小,

  • (4) 釋放動態分配的數組存儲區時,可以使用delete運算符,其語法形式爲:delete [ ]指針變量;

    無須指出空間的大小,但老版本的Cpp要求在delete的方括號中標出數字,以告訴Cpp要釋放多少個元素所佔的空間。例如:delete []pi; delete [10]pi;

  • (5) new可在爲簡單變量分配內存空間的同時,進行初始化。這時的語法形式爲:指針變量=new 類型名(初始值列表)


例 1.12 new爲簡單變量分配內存空間的同時,進行初始化


#include <iostream.h>
int main()
{
    int *p;
    p=new int(99); // 動態分配內存,並將99做爲初始值賦給它
    if (!p)
    {
        cout<<"allocation failure\n";
        return 1;
    }
    cout<<*p;
    delete p;
    return 0;
}
例 1.13 給數組分配內存空間的例子。
#include <iostream.h>
main()
{
    double *s;
    s=new double[10];
    if(!s){
        cout<<"alocation failure\n";
        return 1;
    }
    for(int i=0;i<10;i++)
        s[i]=100.00+2*i;
    for(int i=0;i<10;i++)
        cout<<s[i]<<" ";
    delete []s;
    return 0;
}

15. 引用

(1) 引用的概念


引用就是某一變量(目標)的一個別名,這樣對引用的操做就是對目標的操做。


引用的聲明方法: 類型標識符 &引用名=目標變量名;


int a;
int &ra=a; //定義引用ra,它是變量a的引用,即別名
說明:


  • (1) &在此不是求地址運算,而是起標識做用。

  • (2) 類型標識符是指目標變量的類型。

  • (3)聲明引用時,必須同時對其進行初始化。

  • (4)引用聲明完畢後,至關於目標變量名有兩個名稱。

  • (5)聲明一個引用,不是新定義了一個變量,系統並不給引用分配存儲單元。


例1.15 引用的使用


#include <iostream.h>
void main()
{
    int i;
    int &j=i;
    i=30;
    cout<<"i="<<i<<"j="<<j<<"\n";
    j=80;
    cout<<"i="<<i<<"j="<<j<<"\n";
    cout<<"Address of i"<<&i<<"\n";
    cout <<"Address of j"<<&j<<"\n";
}


結果:


i=30 j=30
i=80 j=80
Address of oxfff4
Address of oxfff4
例1.16 使用引用能夠簡化程序
#include<iostream.h>
main()
{
    int i=15;
    int* iptr=&i;
    int & rptr=i;
    cout<<" i is "<<i<<endl;
    cout<<" *iptr is "<<*iptr<<endl;
    cout<<" rptr is "<<rptr<<endl;
    i=29;
    cout<<" After changing i to 29: "<<endl;
    cout<<" i is "<<i<<endl;
    cout<<" *iptr is "<<*iptr<<endl;
    cout<<" rptr is "<<rptr<<endl;
    return 0;
}


運行結果:


is 15
*iptr is 15
rptr is 15
After changing i to 29
is 29
*iptr is 29
rptr is 29

(2) 引用的使用

  • (1)引用名能夠是任何合法的變量名。除了用做函數的參數或返回類型外,在聲明時,必須當即對它進行初始化,不能聲明完後再賦值。


int i;
int &j;
j=i;


  • (2)引用不能從新賦值,不能再把該引用名做爲其餘變量名的別名,任何對該引用的賦值就是該引用對應的目標變量名的賦值。對引用求地址,就是對目標變量求地址。


int i=5;
int &j1=i; 
int &j2=j1; 


int num=50;
int & ref=num;
int *p=&ref;


  • (3)因爲指針變量也是變量,因此,能夠聲明一個指針變量的引用。方法是: 類型標識符 *&引用名=指針變量名;


#include <iostream.h>
void main()
{
    int *a; //定義指針變量a
    int *&p=a; //定義引用p,初始化爲指針變量a,因此p是a的引用(別名)
    int b=10;
    p=&b; //等價於a=&b,即將變量b的地址賦給a。
    cout<<*a<<endl//輸出變量b的值
    cout<<*p<<endl//等價於cout<<*a;
    cout<<a<<endl//輸出a和p的地址
    cout<<p<<endl;
}


  • (4)引用是對某一變量或目標對象的引用,它自己不是一種數據類型,所以引用自己不佔存儲單元,這樣,就不能聲明引用的引用,也不能定義引用的指針。


int a;
int & & ra=a; //錯誤
int &*p=&ra; //錯誤


  • (5)不能創建數組的引用,由於數組是一個由若干個元素所組成的集合,因此就沒法創建一個數組的別名。

  • (6)不能創建空指針的引用


int &rp=NULL; //錯誤


  • (7)也不能創建空類型void的引用,由於儘管在C++語言中有void數據類型,但沒有任何一個變量或常量屬於void類型。


void &ra=3; //錯誤


  • (8) 儘管引用運算符與地址操做符使用相同的的符號,但時不同的。引用僅在聲明時帶有引用運算符&,之後就像普通變量同樣使用,不能再帶&。其餘場合使用的&都是地址操做符。


int j=5;
int& i=j; // 聲明引用i, "&"爲引用運算符
i=123// 使用引用i,不帶引用運算符
int *pi=&i; // 在此, "&"爲地址操做符
cout<<&pi; // 在此, "&"爲地址操做符

(3) 用引用做爲函數的參數

一個函數的參數也可定義成引用的形式


void swap(int &p1, int &p2) //形參p1, p2都是引用
{
    int p;
    p=p1;
    p1=p2;
    p2=p;
}


在主調函數的調用點處,直接以變量做爲實參進行調用便可,不須要實參變量有任何的特殊要求。


swap(a,b); //直接以a和b做爲實參調用swap函數
例1.17 採用指針參數的例子
#include<iostream.h>
void swap(int *m, int *n)
{
    int temp;
    temp=*m;
    *m= *n;
    *n=temp;
}
main()
{
    int a=5, b=10;
    cout<<"a="<<a<<" b="<<b<<endl;
    swap(&a, &b);
    cout<<"a="<<a<<" b="<<b<<endl;
    return 0;
}


運行結果:


a=5 b=10
a=10 b=5
例1.18 採用「引用參數」傳遞函數參數
#include <iostream.h>
void swap(int& m, int& n)
{
    int temp;
    temp=m;
    m=n;
    n=temp;
}
main()
{
    int a=5, b=10;
    cout<<"a="<<a<<" b="<<b<<endl;
    swap(a, b);
    cout<<"a="<<a<<" b="<<b<<endl;
    return 0;
}


運行結果:


a=5 b=10
a=10 b=5

(4) 用引用返回函數值

函數能夠返回一個引用,將函數說明爲返回一個引用的主要目的是:爲了將函數用在賦值運算符的左邊。


要以引用返回函數值,則函數定義時要按如下格式:


類型標識符 &函數名(形參列表及類型說明)


{函數體}


說明


  • 以引用返回函數值,定義函數時須要在函數名前加&

  • 用引用返回一個函數值的最大好處是,在內存中不產生被返回值的副本。


例1.19 返回引用的函數


#include <iostream.h>
int a[]={1, 3, 5, 7, 9};
intindex(int)// 聲明返回引用的函數
void main()
{
    cout<<index(2)<<endl;
    index(2)=25// 將a[2]從新賦值爲25
    cout<<index(2)<<endl;
}
intindex(int i)
{
    return a[i];
}
例1.20 用引用返回函數的
#include<iostream.h>
int A[10];
intarray(int i);
void main()
{
    int i, number;
    A[0]=0;
    A[1]=1;
    cin>>number;
    for (i=2;i<number;i++)
    {
        array(i)=array(i-2)+array(i-1);
        cout<<"array("<<i<<")="<<array(i)<<endl;
    }
}
intarray(int i)
{
    return A[i];
}
 
 


運行結果:


array(2)=1
array(3)=2
array(4)=3
array(5)=5
array(6)=8
array(7)=13
array(8)=21
array(9)=34


  • 在定義返回引用的函數時,注意不要返回該函數內的自動變量 (局部變量)的引用,因爲自動變量的生存期僅限於函數內部,當函數返回時,自動變量就消失了。


int& fun()
{
    int a;
    //...
    return a;
}


  • 傳遞引用給函數與傳遞指針的效果是同樣的,但使用更簡練。

  • 使用引用傳遞函數的參數,在內存中並無產生實參的副本,它是直接對實參操做;


void swap(int *p1, int *p2)
{
    int p;
    p=*p1; //必須用「*指針變量名」的形式操做目標變量
    p1=*p2;
    *p2=p;
}
main()
{
    int a,b;
    cin>>a>>b;
    swap(&a,&b); //必須以變量a和b的地址做爲實參
    cout<<a<<b;
}

如何使一個被調函數同時返回多個值


因爲函數的返回值是經過函數體中的return語句完成的,但一個return語句只能返回一個值,爲此,咱們能夠採用如下方法:


  • (1)利用全局變量的方法:在函數中把所需數據保存在全局變量中。當被調函數執行完畢後在主調函數中直接讀取全局變量的值便可。

  • (2)使用指針或數組的方法:指針做爲函數參數的狀況下,可將主調函數的某些變量的地址傳遞給被調函數。

  • (3)利用引用的方法:使用引用傳遞參數,能夠在被調函數中改變主調函數中目標變量的值,這種方法實際上就是可使被調函數返回多個值。


例 使用引用使函數返回多個值


如下定義了能夠同時返回10個數中的最大值和最小值的函數max_min。


#include <iostream.h>
void max_min(int *p,int n,int &max,int &min);
//聲明函數max_min
void main()
{
    int a[10];
    int ma,mi;
    int i;
    for(i=0;i<10;i++)
        cin>>a[i];
    max_min(a,10,ma,mi); //調用函數max_min
    cout<<ma<<mi;
}
void max_min(int *p,int n,int &max,int &min)
//形參max 和min定義成引用
{
    int i=0;
    max=*(p+i);
    min=*(p+i);
    for(i=1;i<n;i++) {
        if(max<*(p+i))
            max=*(p+i); //實質上就是對實參變量ma賦值
        if(min>*(p+i))
            min=*(p+i); //實質上就是對實參變量mi賦值
    }
}
例 如下程序中定義了一個普通的函數fn1(它用返回值的方法返回函數值),另一個函數fn2,它以引用的方法返回函數值。


#include <iostream.h>
float temp; //定義全局變量temp
float fn1(float r)//聲明函數fn1
float &fn2(float r)//聲明函數fn2
float fn1(float r) //定義函數fn1,它以返回值的方法返回函數值
{
    temp=(float)(r*r*3.14);
    return temp;
}
float &fn2(float r) //定義函數fn2,它以引用方式返回函數值
{
    temp=(float)(r*r*3.14);
    return temp;
}
void main() //主函數
{
    float a=fn1(10.0);//第1種狀況,系統生成要返回值的副本(即臨時變量)
    float &b=fn1(10.0);//第2種狀況,可能會出錯(不一樣C++系統有不一樣規定)
    //不能從被調函數中返回一個臨時變量或局部變量的引用
    float c=fn2(10.0); //第3種狀況,系統不生成返回值的副本
    //能夠從被調函數中返回一個全局變量的引用
    float &d=fn2(10.0); //第4種狀況,系統不生成返回值的副本
    //能夠從被調函數中返回一個全局變量的引用
    cout<<a<<c<<d;
}


一個返回引用的函數值做爲賦值表達式的左值


通常狀況下,賦值表達式的左邊只能是變量名,即被賦


值的對象必須是變量,只有變量才能被賦值,常量或表達式不能被賦值,但若是一個函數的返回值是引用時,賦值號的左邊能夠是該函數的調用。


例2-26 測試用返回引用的函數值做爲賦值表達式的左值。


#include <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main()
{
    put(0)=10//以put(0)函數值做爲左值, 等價於vals[0]=10;
    put(9)=20//以put(9)函數值做爲左值, 等價於 vals[9]=10;
    cout<<vals[0];
    cout<<vals[9];
}
int &put(int n)
{
    if (n>=0 && n<=9 )
        return vals[n];
    else{
        cout<<」subscript error」;
        return error;
    }
}

用const限定引用


聲明方式:const 類型標識符 &引用名=目標變量名;


用這種方式聲明的引用,不能經過引用對目標變量的值進行修改,從而使引用的目標成爲const,達到了引用的安全性。


例2-27


#include 「iostream.h」
double &fn(const double &pd)
{
    static double ad=32;
    ad+=pd;
    cout<<pd<<endl;
    return ad;
}
void main()
{
    double a=100.0;
    double &pa=fn(a);
    cout<<pa<<endl;
    a=200.0;
    pa=fn(a);
    cout<<pa<<endl;
}


程序運行的結果


100
132
200
332

引用總結


  • (1)在引用的使用中,單純給某個變量取個別名是毫無心義的,引用的目的主要用於在函數參數傳遞中,解決大對象的傳遞效率和空間不如意的問題。

  • (2)用引用傳遞函數的參數,能保證參數傳遞中不產生副本,提升傳遞的效率,且經過const的使用,保證了引用傳遞的安全性。

  • (3)引用與指針的區別是,指針經過某個指針變量指向一個對象後,對它所指向的變量間接操做,程序中使用指針,程序的可讀性差;而引用自己就是目標變量的別名,對引用的操做就是對目標變量的操做。


課後練習題目


#include<iostream.h>
int &max(int &num1,int &num2)// 返回一個較大值
int &min(int &num1,int &num2)// 返回一個較小值
main()
{
    int num1, num2;
    cout<<"Enter the first number: ";
    cin>>num1;
    cout<<"Enter the second number: ";
    cin>>num2;
    max(num1,num2)=0;
    cout<<"\nAfter putting zero in largest, the numbers are";
    cout<<"\n"<<num1<<" and "<<num2<<"\n";
    cout<<"\nNow, please enter two more numbers.\n";
    cout<<"Enter the first number :";
    cin>>num1;
    cout<<"Enter the second number:";
    cin>>num2;
    min(num1, num2)=0;
    cout<<"\nAfter putting zero in smallest the numbers are";
    cout<<"\n"<<num1<<" and "<<num2<<"\n";
    return 0;
}
int &max(int &num1,int &num2)
{
    return (num1>num2)?num1:num2;
}
int &min(int &num1,int &num2)
{
    return (num1<num2)?num1:num2;}
相關文章
相關標籤/搜索