互聯(lián)網(wǎng)定制網(wǎng)站網(wǎng)站優(yōu)化排名推廣
專欄簡介:本專欄主要面向C++初學(xué)者,解釋C++的一些基本概念和基礎(chǔ)語言特性,涉及C++標準庫的用法,面向?qū)ο筇匦?#xff0c;泛型特性高級用法。通過使用標準庫中定義的抽象設(shè)施,使你更加適應(yīng)高級程序設(shè)計技術(shù)。希望對讀者有幫助!
目錄
- 自定義數(shù)據(jù)結(jié)構(gòu)
- 定義Sales_data類型
- 類數(shù)據(jù)成員
- 使用Sales_data類
- 添加兩個Sales_data對象
- Sales_data對象讀入數(shù)據(jù)
- 編寫自己的頭文件
- 預(yù)處理器概述
自定義數(shù)據(jù)結(jié)構(gòu)
從最基本的層面理解,數(shù)據(jù)結(jié)構(gòu)是把一組相關(guān)的數(shù)據(jù)元素組織起來然后使用它們的策略和方法。舉一個例子,我們的Sales_item類把書本的TSBN編號、售出量及銷售收入等數(shù)據(jù)組織在了一起,并且提供諸如isbn函數(shù)、>>、<<、+、+=等運算在內(nèi)的一系列操作,Sales_item類就是一個數(shù)據(jù)結(jié)構(gòu)。
C++語言允許用戶以類的形式自定義數(shù)據(jù)類型,而庫類型string、istream、ostream等也都是以類的形式定義的,就像Sales_item類型一樣。C++語言對類的支持甚多,事實上本書的第III部分和第IV部分都將大篇幅地介紹與類有關(guān)的知識。盡管Sales_item類非常簡單,但是要想給出它的完整定義可在第14章介紹自定義運算符之后。
定義Sales_data類型
盡管我們還寫不出完整的Sales_item類,但是可以嘗試著把那些數(shù)據(jù)元素組織到一起形成一個簡單點兒的類。初步的想法是用戶能直接訪問其中的數(shù)據(jù)元素,也能實現(xiàn)一些基本的操作。
既然我們籌劃的這個數(shù)據(jù)結(jié)構(gòu)不帶有任何運算功能,不妨把它命名為Sales_data以示與Sales_item的區(qū)別。Sales_data初步定義如下:
struct Sales_data{std::strtng bookNo;unsigned units_sold = 0;double revenue = 0.0;
}
我們的類以關(guān)鍵字struct開始,緊跟著類名和類體(其中類體部分可以為空)。類體由花括號包圍形成了一個新的作用域。類內(nèi)部定義的名字必須唯一,但是可以與類外部定義的名字重復(fù)。類體右側(cè)的表示結(jié)束的花括號后必須寫一個分號,這是因為類體后面可以緊跟變量名以示對該類型對象的定義,所以分號必不可少:
struct Sales_data{/*…*/}accum,trans,*salesptr;
//與上一條語句等價,但可能更好一些
struct Sales_data{}
Sales_data acoum , trans , *salesptr;
分號表示聲明符的結(jié)束。一般來說,最好不要把對象的定義和類的定義放在一起。這么做無異于把兩種不同實體的定義混在了一條語句里,一會兒定義類,一會兒又定義變量,顯然這是一種不被建議的行為。
WARNING: 很多新手程序員經(jīng)常忘了在類定義的最后加上分號。
類數(shù)據(jù)成員
類體定義類的成員,我們的類只有數(shù)據(jù)成員(data member)。類的數(shù)據(jù)成員定義了類的對象的具體內(nèi)容,每個對象有自己的一價數(shù)據(jù)成員拷貝。修改一個對象的數(shù)據(jù)成員,不會影響其他Sales_data的對象。
定義數(shù)據(jù)成員的方法和定義普通變量一樣:首先說明一個基本類型,隨后緊跟一個或多個聲明符。我們的類有3個數(shù)據(jù)成員:一個名為 bookNo 的 string 成員、一個名為 units_sold 的unsigned 成員和一個名為 revenue 的 double 成員。又個Sales_data的對象都將包括這3個數(shù)據(jù)成員。
C++11新標準規(guī)定,可以為數(shù)據(jù)成員提供一個類內(nèi)初始值(in-class initializer)。創(chuàng)建對象時,類內(nèi)初始值將用于初始化數(shù)據(jù)成員。沒有初始值的成員將被默認初始化。因此當定義Sales_data的對象時,units_sold和revenue都將初始化為0,bookNo將初始化為空字符串。
對類內(nèi)初始值的限制與之前介紹的類似:或者放在花括號里,或者放在等號右邊,記住不能使用圓括號。
使用Sales_data類
和Sales_item類不同的是,我們自定義的Sales_data類沒有提供任何操作,Sales_data類的使用者如果想執(zhí)行什么操作就必須自己動手實現(xiàn)。。程序的輸入是下面這兩條交易記錄:
0-201-78345-X320.00
0-201-78345-X225.00
每筆交易記錄著圖書的ISBN編號、售出數(shù)量和售出單價。
添加兩個Sales_data對象
因為sales_data類沒有提供任何操作,所以我們必須自己編碼實現(xiàn)輸入、輸出和相加的功能。假設(shè)已知Sales_data類定義于sales_data.h文件內(nèi),將詳細介紹定義頭文件的方法。
因為程序比較長,所以接下來分成兒部分介紹。總的來說,程序的結(jié)構(gòu)如下:
#nclude<iostreami>
#include<string>
#include“Sales_data.h“
ntmain()
{Sales_data data1,data2;//讀入data1 和 data2的代碼//datal和data2的ISBN是否相同的代碼//如果相同,求data1和data2的總和
}
和原來的程序一樣,先把所需的頭文件包含進來并且定義變量用于接受輸入。和Sales_item類不同的是,新程序還包含了string頭文件,因為我們的代碼中將用到string類型的成員變量bookkNo。
Sales_data對象讀入數(shù)據(jù)
第3章和第10章將詳細介紹string類型的細節(jié),在此之前,我們先了解一點兒關(guān)于string的知識以便定義和使用我們的ISBN成員string類型其實就是字符的序列,它的操作有>>、<<和==等,功能分別是讀入字符串、寫出字符串和比較字符串。這樣我們就能書寫代碼讀入第一筆交易了:
double price=0;//書的單價,用于計算銷售收入//讀入第1筆交易:ISBN、銷售數(shù)量、單價std::cin >> data1.bookNo >> datal.units_sold >> price;//計算銷售收入datal.revenue = datal.units_sold * price;
交易信息記錄的是書售出的單價,而數(shù)據(jù)結(jié)構(gòu)存儲的是一次交易的銷售收入,因此需要將單價讀入到double變量price,然后再計算銷售收入revenue。輸入語句
std::cin >> data1.bookNo >> data1.units_sold >> price;
使用點操作符讀入對象 data1 的bookNo成員和unitssold成員。最后一條語句把datal.units_sold和price的乘積賦值給data1的revenue成員。接下來程序重復(fù)上述過程讀入對象data2的數(shù)據(jù):
//讀入第2筆交易std::cin>>data2.bookNo>>data2.units_sold>>price;data2.revenue=data2.units_sold * price;
輸出兩個Sales_data對象的和
剩下的工作就是檢查兩筆交易涉及的ISBN編號是否相同了.如果相同輸出它們的和,否則輸出一條報錯信息:
if(datal.bookNo == _data2.bookNo) {unsigned totalCnt =datal.units_sold+data2.units_sold;double totalRevenue=datalrevenue+data2.revenue;//輸出:ISBN、總銷售量、總銷售額、平均價格std::cout<<datal.bookNo<<““<<totalCnt <<““<<totalRevenue<<““if(totalCnt!=0) {std::cout<<totalRevenue/totalCnt<<std::endl;}elsestd::cout<<“(nosales)“<<std::endl;return 0;//標示成功
} else {//兩筆交易的ISBN不一樣 std::cerr<<“Data must refer to the same ISBN“ << std::endl;return -1;//標示失敗
}
在第一個if語句中比較了daata1和data2的bookNo成員是否相同。如果相同則執(zhí)行第一個if語句花括號內(nèi)的操作,首先計算units_sold的和并賦給變量totalCnt,然后計算revenue的和并賦給變量totalRevenue,輸出這些值。接下來檢查是否確實售出了書籍,如果是,計算并輸出每本書的平均價格;如果售量為零,輸出一條相應(yīng)的信息。
眼下先把Sales_data類的定義和 main 函數(shù)放在同一個文件里。
編寫自己的頭文件
類一般都不定義在函數(shù)體內(nèi)。當在函數(shù)體外部定義類時,在各個指定的源文件中可能只有一處該類的定義。而且,如果要在不同文件中使用同一個類,類的定義就必須保持一致。
為了確保各個文件中類的定義一致,類通常被定義在頭文件中,而且類所在頭文件的名字應(yīng)與類的名字一樣。例如,庫類型string在名為string的頭文件中定義。又如,我們應(yīng)該把Sales_data類定義在名為sales_data.h的頭文件中。
頭文件通常包含那些只能被定義一次的實體,如類、const和constexpr變量等。頭文件也經(jīng)常用到其他頭文件的功能.例如,我們的Sales_data類包含有一個string成員,所以Sales_data.h必須包含string.h頭文件。同時,使用Sales_data類的程序為了能操作bookkNo成員需要再一次包含string.h頭文件。
這樣,事實上使用Sales_data類的程序就先后兩次包含了string.h頭文件:一次是直接包含的,另有一次是隨著包含Sales_data.h被隱式地包含進來的。有必要在書寫頭文件時做適當處理,使其遇到多次包含的情況也能安全和正常地工作。
頭文件一旦改變,相關(guān)的源文件必須重新編譯以獲取更新過的聲明。
預(yù)處理器概述
確保頭文件多次包含仍能安全工作的常用技術(shù)是預(yù)處理器(preprocessor),它由C++語言從C語言繼承而來。預(yù)處理器是在編譯之前執(zhí)行的一段程序,可以部分地改變我們所寫的程序。之前已經(jīng)用到了一項預(yù)處理功能include,當預(yù)處理器看到#include標記時就會用指定的頭文件的內(nèi)容代替#include。
C++程序還會用到的一項預(yù)處理功能是頭文件保護符(header guard),頭文件保護符依賴于預(yù)處理變量。預(yù)處理變量有兩種狀態(tài):已定義和未定義。#define 指令把一個名字設(shè)定為預(yù)處理變量,另外兩個指令則分別檢查某個指定的預(yù)處理變量是否已經(jīng)定義: #ifdef 當且僅當變量已定義時為真,#ifndef當且僅當變量未定義時為真。一旦檢查結(jié)果為真,則執(zhí)行后續(xù)操作直至遇到#endif指令為止。
使用這些功能就能有效地防止重復(fù)包含的發(fā)生:
#ifndef SRLES_DATA_H
#define SRLES_DATA_H
#include<string>
struct Sales_data{std::string bookNo;unsigned units_sold=0;double revenue=0.0;
}
第一次包含Sales_data.h 時,#ifndef的檢查結(jié)果為真,預(yù)處理器將順序執(zhí)行后面的操作直至遇到#endif為止。此時,預(yù)處理變量SALES_DATA_H的值將變?yōu)橐讯x,而且sales_data.h也會被拷貝到我們的程序中來.后面如果再一次包含sales_data.h,則 #ifndef的檢查結(jié)果將為假,編譯器將忽略#ifndef到#endif之間的部分。
WARNING: 預(yù)處理變量無視C++語言中關(guān)于作用域的規(guī)則。
整個程序中的預(yù)處理變量包括頭文件保護符必須唯一,通常的做法是基于頭文件中類的名字來構(gòu)建保護符的名字,以確保其唯一性。為了避免與程序中的其他實體發(fā)生名字沖突,一般把預(yù)處理變量的名字全部大寫。