C++動(dòng)態(tài)內(nèi)存分配、名稱空間、全局變量、常量、字符串基礎(chǔ)、類型轉(zhuǎn)換、回調(diào)函數(shù)
#include <iostream>
#include <string> //<string>頭文件不是<string.h>頭文件,在c++中<string.h>被調(diào)整為<cstring>,<string>是提供string類的相關(guān)函數(shù)的頭文件
int mainnew1()
{
???? int* pi = new int; //new關(guān)鍵字,相當(dāng)于(int*)malloc(sizeof(int)) ,返回動(dòng)態(tài)內(nèi)存地址,需要用delete關(guān)鍵字釋放
???? *pi = 20; //賦值
???? delete pi; //相當(dāng)于free(pi)
???? int* pi2 = new int(20); //初始化, new 類型(值)
???? delete pi2;
???? struct xy { int x; int y; };
???? xy* pxy = new xy; //new是動(dòng)態(tài)內(nèi)存分配,返回的是地址,所以需要指針接收
???? pxy->x = 20;
???? delete pxy;
???? xy* pxy2 = new xy{ 1,2 }; //初始化結(jié)構(gòu)使用{}
???? delete pxy2;
???? int* pia = new int[5]; //創(chuàng)建數(shù)組,同樣使用int*指針接收,new int用int*接收,new int[5]也用int*接收,相當(dāng)于(int*)malloc(5*sizeof(int))
???? pia[1] = 20;
???? delete[]pia; //數(shù)組釋放寫作 delete[]變量名 ,無論幾維數(shù)組都只寫一個(gè)[]
???? //new關(guān)鍵字和new[]關(guān)鍵字是兩個(gè)關(guān)鍵字,delete和delete[]同理
???? int* pia2 = new int[5] {0}; //數(shù)組初始化使用{}
???? memset(pia2, 0, sizeof(int[5])); //常用memset(內(nèi)存地址,初始化值,size)進(jìn)行初始化
???? delete[]pia2;
???? char* pc = new char; //創(chuàng)建char大小內(nèi)存,使用char*接收地址
???? delete pc;
???? char** ppc = new char*(NULL); //創(chuàng)建char*大小內(nèi)存,使用char**接收地址
???? delete ppc;
???? char** parr = new char* [5]{NULL}; //創(chuàng)建char*[5]指針數(shù)組大小的內(nèi)存,使用char**接收地址,數(shù)組初始化NULL對應(yīng)指針元素
???? delete[]parr;
???? char(*pca)[4] = new char[3][4]{{'a'}}; //創(chuàng)建二維數(shù)組,指針指向數(shù)組的每個(gè)元素(即一維數(shù)組),初始化{{},{},{}}二維所以兩層大括號
???? delete[]pca;
???? char* ptr = NULL;
???? delete ptr; //delete可以用于指向null的指針
???? ptr = NULL;
???? delete[] ptr; //delete[]也可以
???? return 0;
}
#include <new> //new頭文件包含定位new運(yùn)算符
char staticmem[256];
void mainnew2()
{
???? int* ptr = new(staticmem)int[10]; //定位new運(yùn)算符,使用new后面括號內(nèi)指定的地址(void*)存放int[10]內(nèi)存塊并返回內(nèi)存塊的地址,所以ptr的值和staticmem的值相同,由于沒有對內(nèi)存塊進(jìn)行初始化,所以該區(qū)域依然保持全局變量默認(rèn)的零初始化值
???? ptr[0] = 97; //修改第一個(gè)int元素,值0-255對應(yīng)低階8位第一個(gè)字節(jié),所以后面三個(gè)字節(jié)沒有改變
???? std::cout << staticmem[0] ; //結(jié)果為a,char數(shù)組第一個(gè)元素即內(nèi)存塊中第一個(gè)字節(jié)修改為97,說明這里組成int的四個(gè)字節(jié)按地址最前面的字節(jié)最低階,最后面的字節(jié)最高階
???? //定位new運(yùn)算符使用已有內(nèi)存塊來分配內(nèi)存,所以不需要單獨(dú)釋放delete
}
inline void clean_input() //內(nèi)聯(lián)函數(shù),向編譯器申請將該函數(shù)轉(zhuǎn)換為內(nèi)聯(lián)代碼,編譯器根據(jù)情況決定,c++推薦使用內(nèi)聯(lián)函數(shù)取代類函數(shù)宏的內(nèi)聯(lián)代碼
{
???? while (getchar()!='\n')
???? {
???????? continue;
???? }
}
int globali; //全局變量
short globals; //沒有顯式初始化的靜態(tài)變量會初始化為0
double globald;
char globalc;
int globali2 = 10; //顯示初始化,靜態(tài)變量在顯示初始化之前也會被零初始化
const int constantinternal = 10; //在c++中const修飾的全局變量默認(rèn)帶有static,內(nèi)部鏈接,這使得在頭文件中聲明的常量會在每個(gè)包含的源文件中產(chǎn)生一個(gè)備份,這和c中不同(c中不默認(rèn)帶),c++中定義常量必須進(jìn)行初始化,不允許默認(rèn)零初始化
extern const int constexternal = 20; //如果要使用外部鏈接的常量,需要使用extern const,這種寫法后面不帶初始化則為引用式聲明,后面帶初始化則為定義式聲明,同樣只能存在一個(gè)定義
int mainnamespace1()
{
???? std::cout << ::globali<<std::endl; //::作用域解析運(yùn)算符,用法 名稱空間::標(biāo)識符 ,cout為std名稱空間中定義,globali在當(dāng)前文件中定義為全局變量并且沒有命名名稱空間,所以::前為空
???? std::cout << "enter:";
???? ::clean_input(); //普通函數(shù)調(diào)用省略::
???? using std::cout; //using聲明,將特定的名稱添加到它所屬的聲明區(qū)域中,載入std名稱空間中的cout,當(dāng)前作用域內(nèi)從當(dāng)前語句以下部分有效,using聲明添加的標(biāo)識符不能在當(dāng)前聲明區(qū)域中再被用作標(biāo)識其他變量,同樣,在同一個(gè)聲明區(qū)域中已經(jīng)被使用的同名標(biāo)識符不能使用using聲明添加,using聲明在使用方面就像是聲明了一個(gè)變量(指不允許當(dāng)前作用域重名)
???? //報(bào)錯(cuò):int cout = 29; 與using聲明沖突
???? cout << globali; //全局變量調(diào)用省略::
???? return 0;
}
namespace namespace1 { //定義名稱空間
???? int x = 10; //全局變量
???? struct ss { int i; }; //結(jié)構(gòu)
???? void func() { /*函數(shù)略*/}
???? namespace inner { int innerx; } //名稱空間
}
namespace namespace1 { //同名的名稱空間會被合并,如在頭文件和類庫中使用相同的名稱空間包裹,才能通過該名稱空間調(diào)用
????int y = 2;
}
namespace namespace2 { //不同的名稱空間
???? int x = 3;//兩個(gè)名稱空間中的x不沖突
???? int y = 9;
}
int mainnamespace2()
{
???? using namespace std; //using編譯指令,載入std名稱空間所有內(nèi)容,如果當(dāng)前作用域內(nèi)有重名標(biāo)識符可能產(chǎn)生混亂
???? cout << namespace1::x << " " << namespace2::x << endl; //兩個(gè)名稱空間的x不沖突
???? {
???????? using namespace namespace1; //通過創(chuàng)建塊作用域來暫時(shí)載入
???????? x = 300;
???? }
???? //離開塊以后不再能直接調(diào)用x,但之前的更改依舊有效,因?yàn)槭侨肿兞?/span>
???? {
???????? using namespace namespace2;
???????? x = 400;
???? }
???? cout << namespace1::x << " " << namespace2::x << endl; //兩個(gè)x都改變了
???? //{
???????? // using namespace namespace1; //如果同時(shí)載入兩個(gè)名稱空間
???????? // using namespace namespace2;
???????? // x = 20; //在使用兩個(gè)名稱空間中同名變量時(shí)報(bào)錯(cuò)
???? //}
???? return 0;
}
int var1; //全局名稱空間的全局變量var1
namespace ns3 { int var1; } //名稱空間ns3中的變量var1
void mainnamespace3()
{
???? using std::cout; //using聲明
???? using std::endl;
???? int var1; //局部變量var1;
???? cout << &var1 << endl; //使用局部變量
???? cout << &::var1 << endl; //使用全局變量
???? cout << &ns3::var1 << endl; //使用名稱空間ns3中的變量
???? using namespace ns3; //using編譯指令不同于using聲明,與局部變量名沖突不會直接報(bào)錯(cuò)但會增加用錯(cuò)的幾率
???? cout << &var1 << endl; //依然使用局部變量,局部變量會蓋住名稱空間中的同名變量,其他名稱不沖突的變量可以正常訪問
???? cout << &::var1 << endl; //使用全局變量
???? cout << &ns3::var1 << endl; //雖然被局部變量蓋住,但依然可以通過作用域解析運(yùn)算符來使用名稱空間ns3中的變量
}
//如果在全局聲明區(qū)域中使用using namespace ns3; 會使其他函數(shù)調(diào)用全局var1時(shí)產(chǎn)生二義性報(bào)錯(cuò)
double var3=10;
/*------test_namespace.h-----*/
namespace ns5 {
???? //namespace在頭文件中使用時(shí)也要遵循頭文件的用法,不應(yīng)在頭文件中定義外部鏈接的靜態(tài)變量(這會導(dǎo)致包含該頭文件的多個(gè)源文件重復(fù)定義沖突)
???? struct ns5struct { int a; }; //頭文件中的名稱空間中聲明結(jié)構(gòu)
???? void ns5func(); //頭文件中不包含普通函數(shù)的定義
???? extern double var3; //引用在源文件中定義的ns5::var3變量,在名稱空間中直接使用的標(biāo)識符默認(rèn)為該名稱空間::標(biāo)識符,這個(gè)引用不會引用到::var3當(dāng)前文件的全局變量,所以如果要引用當(dāng)前文件的全局變量應(yīng)寫作extern double ::var3
}
/*------test_namespace.cpp-----*/
namespace ns5 {
???? //在cpp文件中追加同一名稱空間的內(nèi)容:函數(shù)定義、定義式聲明等
???? double var3 = 20; //在頭文件中引用的var3會使用這個(gè)定義式聲明,任何包含頭文件的翻譯單元都能使用ns5::var3,但如果頭文件中沒有引用式聲明ns5::var3,就和普通外部鏈接變量一樣沒有引用式聲明就無法使用該變量
???? void ns5func() {}
}
void test_string2()
{
???? using namespace std;
???? string s1 = "c++中使用string類表示字符串"; //使用string不再需要擔(dān)心字符數(shù)組索引越界
???? string s2("另一種初始化方式");
???? string s3(s1); //使用變量初始化
???? string* pstr = new string; //new返回地址,所以需要string*接收
???? delete pstr;
???? string* pstr2 = new string("asdf"); //動(dòng)態(tài)分配一塊內(nèi)存以存儲string,并初始化為"asdf",后將地址返回,通過指針接收
???? delete pstr2;
???? s1 += s2; //將s2字符串拼接到s1后面
???? cout << s1.size() << endl; //返回s1的長度,不包括\0
???? if (s1 == s2) //相當(dāng)于strcmp==0
???? ????cout << "s1和s2內(nèi)容相同" << endl;
???? else if (s1 < s2) //相當(dāng)于strcmp<0
???? {
???? ????cout << "根據(jù)字符集順序s1<s2" << endl;
???? }
???? s1[1] = '-'; //和字符數(shù)組同樣可對每個(gè)字符進(jìn)行更改
???? const char* pc = s3.c_str(); //.c_str()返回字符串的地址
???? s1.swap(s3); //將s1和s3交換
???? cout << s1.find('+') << endl; //.find(char,size_t)在s1中查找參數(shù)1,找到返回索引,沒找到返回-1,有參數(shù)2默認(rèn)為0即從頭開始找,結(jié)果1
???? cout << s1.find('+', 2) << endl; //從索引2開始找,結(jié)果2
???? cout << s1.find("++") << endl; //.find(char*,size_t)重載,查找字符串
???? string strarr[5] = { //string數(shù)組,每個(gè)元素都是string
???????? "1",
???????? "12",
???????? "123",
???????? "1234",
???????? "12345"
???? };
???? s1.erase(0, s1.find_first_not_of('c')-0); //.erase(size_t,size_t)函數(shù)對字符串進(jìn)行刪節(jié),從參數(shù)1指定的下標(biāo)/索引位開始刪,參數(shù)2指定刪多少個(gè),.find_first_not_of(char)查找第一個(gè)不是參數(shù)1字符的字符,返回下標(biāo),c下標(biāo)為0,返回'+'的下標(biāo)1,.erase(0,1)從0下標(biāo)開始刪,刪1個(gè),新的字符串比之前的少第一個(gè)字符
???? s1.erase(s1.find_last_not_of(' ') + 1); //參數(shù)2默認(rèn)size_t最大值,即會一直刪到\0為止,查找最后一個(gè)不是空格的字符返回下標(biāo),為修剪掉字符串末尾的空格,需要從該下標(biāo)的下一個(gè)下標(biāo)開始刪所以+1
???? s1.erase(); //參數(shù)1默認(rèn)0,參數(shù)2默認(rèn)最大,執(zhí)行后s1字符串為空字符串
???? string sss1("ying"); //用字符串為string傳參
???? sss1 = sss1 + "yyy"; //拼接字符串
???? sss1 = "xxx" + sss1; //也可以將字符串拼接到string對象前面
???? cin >> s1; //>>抽取運(yùn)算符持續(xù)讀取一個(gè)單詞即停,這種用法string對象不能存儲一整行帶空格內(nèi)容
???? getline(cin, s1); //<string>頭文件中的函數(shù),使用istream對象讀取一整行存儲到string對象中
???? sss1.compare(""); //和參數(shù)字符串比較,相同返回0不同返回1
}
void check(std::string& ss)
{
???? const char* ptr;
???? while (std::cout << "q to quit:", getline(std::cin, ss)) //全局函數(shù)getline(cin,string)返回cin,在while條件判斷中用于判斷讀取是否成功
???? {
???????? if (1 == ss.size() && 'q' == ss[0]) //也可以判斷 !ss.compare("q")
???????? break;
???????? ptr = ss.c_str();
???????? while (*ptr)
???????? {
???????? ????std::cout.put(toupper(*ptr++));
???????? }
???????? if (ss.size()) //讀取到空行時(shí).size()==0
???????? {
???????? ????std::cout << std::endl;
???????? }
???? }
}
int maincast1()
{
???? float f;
???? int i = 10;
???? f = i; //隱式轉(zhuǎn)換
???? f = (float)i; //c強(qiáng)轉(zhuǎn)
???? f = static_cast<float>(i); //c++顯式轉(zhuǎn)換,編譯時(shí)會檢查目標(biāo)類型和源類型是否相關(guān)
???? void* pv;
???? int* pi = NULL;
???? double* pd;
???? pv = pi; //隱式
???? pv = static_cast<void*>(pi);????//顯示轉(zhuǎn)換
???? pd = reinterpret_cast<double*>(pi); //c++強(qiáng)轉(zhuǎn)對內(nèi)存重新解釋,使用reinterpert_cast,不能刪除const特性,不能將源類型轉(zhuǎn)換為size更小的類型,不能將函數(shù)指針和數(shù)據(jù)指針相互轉(zhuǎn)換。
???? const char* pc = "sss";
???? char* pnc = const_cast<char*>(pc); //將const的指針/引用解除const限定,比如有一個(gè)func(char*)讀取非const參數(shù),但是函數(shù)內(nèi)部并不改變該指針,要想使用這個(gè)函數(shù)就需要const_cast脫掉const,<>中的類型需要和原類型除const部分相同,這里是const char*轉(zhuǎn)char*,對于const值通過const_cast刪除const特性后進(jìn)行修改的結(jié)果是不確定的
???? int ii = 20;
???? const int* pii = ⅈ
???? int* piii = const_cast<int*>(pii); //但對于const指針指向的非const值,使用const_cast刪除指針的const特性進(jìn)行修改值是允許的
???? *piii = 30; //因?yàn)閕i是非const所以可以修改
???? //錯(cuò)誤:double* pdd = const_cast<double*>(pii); const_cast只能修改const/volatile特性,其他部分需要和原類型相同
???? //dynamic_cast動(dòng)態(tài)轉(zhuǎn)換后續(xù)介紹
???? return 0;
}
double add01(double d1, double d2) { return d1 + d2; }
double calculate01(double d1, double d2, double (*callback_func)(double, double)) //接收一個(gè)函數(shù)指針參數(shù)
{
????return callback_func(d1, d2); //在函數(shù)內(nèi)調(diào)用接收的函數(shù)指針來處理變量
}
void maincallback1()
{
???? double d1 = 10.0;
???? double d2 = 10.2;
???? calculate01(d1, d2, add01); //main為主調(diào)函數(shù),calculate01為被調(diào)函數(shù),add01為回調(diào)函數(shù),calculate01向主調(diào)函數(shù)索要一個(gè)回調(diào)函數(shù)用于函數(shù)內(nèi)部處理數(shù)據(jù),用戶通過定義回調(diào)函數(shù)決定calculate01的行為
}