C/C++(C++內存管理,內聯函數,類型轉換,命名空間,string類)
內存管理
new/delete
C語言中提供了 malloc 和 free 兩個系統函數,#include
struct Stu
{
int age;
string name;
};
Stu* pStu = new Stu{23,"assassin"};
cout<
char *p = new int[4];
strcpy(p,"china");
cout<<p<<endl;
int *pi = new int[5];
//int *pi = new int[5]{0};//初始化0,一般不這樣
memset(pi,0,sizeof(int[5]));//初始化
for(int i = 0 ;i < 5;i++)
{
cout<<pi[i]<<endl;
}
生成指針數組:
char **p = new char*[5]{NULL};
p[0] = "assassin";
p[1] = "automan";
p[2] = "wunworld";
while(*p)
{
cout<<*p++<<endl;
}
/*
assassin
automan
wunworld
*/
生成二維數組;
int (*pa)[4] = new int[3][4]{{0}};//初始化
for(int i = 0;i < sizeof(int [3][4])/sizeof(int[4]);i++)
{
for(int j = 0;j < 4;j++)
{
cout<<pa[i][j]<<" ";
}
cout<<endl;
}
/*
0 0 0 0
0 0 0 0
0 0 0 0
*/
//多維
int (*ppp) [3][4][5] = new int[2][3][4];
釋放delete:
int *p = new int;
delete p;
int *a = new int[100];
delete []a;//正確的釋放
delete a;//只把第一個釋放了
int (*aa)[4 ] = new int[3][4];
delete []aa;//多位的數組也是一個方括號,底層用遞歸
註意事項
1,new/delete 是關鍵字,效率高於 malloc 和 free.
2,配對使用,避免內存泄漏和多重釋放。
3,避免,交叉使用。比如 malloc 申請的空間去 delete,new 出的空間被 free;
//c
int *p = (int *)malloc(100);
if(NULL == p)
return -1;
//c++
int *pi = new (std::nothrow) int[100];
if(pi == NULL)
return -1;
內聯函數(inline function)
c 語言中有宏函數的概念。宏函數的特點是內嵌到調用代碼中去,避免了函數調用的開銷。但是由於宏函數的處理發生在預處理階段,缺失了語法檢測和有可能帶來的語意差錯,容易使得內存text段體積變大,不會類型檢查。
#define SQR(i) ((i)*(i))
int main()
{
int i=0;
while(i<5)
{
// printf("%d\n",SQR(i++));
printf("%d\n",sqr(i++));
}
return 0;
}
int sqr(int i)
{
return i*i;
}
/*
優點:一段高度抽象的邏輯,不易產生歧義,是的text段體積變小,會類型檢查。
缺點:函數調用的壓棧與出棧的開銷
*/
內聯函數兼有宏和函數的優點。系統自己有一套優化方案,inline變成了給編譯器的一種建議。
inline int sqr(int i)
{
return i*i;
}
評價
優點:避免調用時的額外開銷(入棧與出棧操作)
代價: 由於內聯函數的函數體在代碼段中會出現多個“副本”,因此會增加代碼段的空間。
本質:以犧牲代碼段空間為代價,提高程序的運行時間的效率。
適用場景:函數體很“小”,且被“頻繁”調用。
強制類型轉化
static_cast
//對於隱時類型可以轉化的
reinterpret_cast
//對於無隱式的類型轉化,static_cast不可用
const_cast
//
dynamic_cast
//
static_cast
float a = 5.6;
int b = 5;
b = static_cast<int>(a);//把a轉成int賦值給b
void *p,int *q;
p = q;//可以
q = p;//不可以,任何指針都可以賦值給void *類型,但是void *類型不可以賦值給指針類型。
q = static_cast<int*>(p);
reinterpret_cast
int a[5] = {1,2,3,4,5};
int *p = reinterpret_cast<int*>((reinterpret_cast<int>(a)+1));
const_cast
( 脫) 常類型轉換,只能引用與指針與引用
const 一定不可以修改
void func(const int &r)
{
}
void func2(int & v)
{
cout<<v<<endl;
}
int main()
{
const int a = 19;
func(10);//可以
func(a+10);//可以,應為參數中有const修飾。
func2(const_cast<int&>(a));//因為a是const int類型
int & ra = const_cast<int&>(a);
ra = 200;
cout<<"a = "<<a<<" ra= "<<ra<<endl;
cout<<"&a = "<<&a<<" ra = "<<&ra<<endl;
/*
a = 19 ra = 200
&a = 0x... 不一樣
*/
}
用來移除對象的常量性(cast away the constness)使用 const_cast 去除 const 限定的目的不是為了修改它的內容,使用 const_cast 去除 const 限定,通常是為了函數能夠接受這個實際參數。
可以改變 const 自定義類的成員變量,但是對於內置數據類型,卻表現未定義行為 .
const 常變量
#defined N 200 //宏,在於處理的發生了替換
const int a = 100;//編譯階段發生了替換
//const 永遠不會發生改變
命名空間(namespace scope)
//全局無名空間
int v = 55;
int main()
{
int *p = &v;//訪問全局的
int v =5;
cout<<v<<endl;//5
cout<<*p<<endl;//55
cout<<::v<<endl;//::作用域運算符,前面要命名空間,平常調配用的函數之前省略::
return 0;
}
namespace 是對全局命名空間的再次劃分
#include<iostream>
using namespace std;
namespace Spac{
int a;//全局變量
struct Stu{};//數據類型
void func();//函數
namespace//其它命名空間
}
使用:
#include<iostream>
using namespace std;
namespace Space {
int x;
int y;
}
namespace Other {
int x;
int y;
}
int main()
{
Space::x = 200;//這種與本地的不會沖突
cout<<Space::x<<endl;//200
using Space::x;//在這作用域內的x都是Space命名空間中的x
x = 20;
cout<<x<<endl;
using Space Space;//直接把Space這個命名空間打開
x = 20;
y = 30;
return 0;
}
各種之間沖突解決
利用最小單位的作用域塊
#include<iostream>
using namespace std;//標中庫空間命名
namespace Space {
int x;
int y;
}
namespace Other {
int x;
int y;
}
int main()
{
{
using Space Space;
x = 20;
y = 30;
}
{
using Space Other;
x = 50;
y = 70;
}
int x,y;
return 0;
}
支持嵌套
namespace Space {
int x;
int y;
namespace Other {
int m;
int n;
}
}
int main()
{
using namespace Space::Other;//使用Other命名空間中的變量,不建議嵌套
m = 30;
return 0;
}
協同開發中的使用
namespace Space {
int x;
}
namespace Space {
int y;
}//命名空間相同會自動合並
int main()
{
using namespace Space;
x = 10;
y = 20;
return 0;
}
系統string類
string是一個類而非關鍵字,初始化比較靈活(類似js中var)
string s = "assassin";//賦值
string s2 = "assassin1";
cout<<s2.size()<<endl;//大小
string s3 = "wunworld";
s3 += s2;//拼接
if(s == s2)//比較
cout<<"s = s2"<<endl;
else if(s > s2)
cout<<"s > s2"<<endl;
else
cout<<"s < s2"<<endl;
char buf[1024];
strcpy(buf,s.c_str());
//如果和字符連用時,一定用c_str(),返回的是char *類型
//交換swap(string &s2)成員函數
s.swap(s2);
查找:
int find(char c, int pos = 0);
int find(char * s, int pos = 0);
//返回下標值,沒有找到返回-1,默認從 0 下標開找
eg:
string s = "assassin";
int n = s.find("i",0);
cout<<n<<endl;//6
string 類型數組
string sArray[10] = {
"0",
"1",
"22",
"333",
"4444",
"55555",
"666666",
"7777777",
"88888888",
"999999999",
};
for(int i=0; i<10; i++)
{
cout<<sArray[i]<<endl;
}
string 數組是高效的,如果用二維數組來存入字符串數組的話,則容易浪費空間,此時列數是由最長的字符串決定。如果用二級指針申請堆空間,依據大小申請相應的空間,雖然解決了內存浪費的問題,但是操作麻煩。用 string 數組存儲,字符串數組的話,效率即高又靈活。
學習C++的建議:
1、在 C++中幾乎不需要用宏,用 const 或 enum 定義顯式的常量,用 inline 避免函數 調用的額外開銷,用模板去刻畫一族函數或類型,用 namespace 去避免命名沖突。
2、不要在你需要變量之前去聲明,以保證你能立即對它進行初始化。
3、不要用 malloc,new 運算會做的更好。
4、避免使用 void*、指針算術、聯合和強制,大多數情況下,強制都是設計錯誤的指示器。
5、盡量少用數組和 C 風格的字符串,標準庫中的 string 和 vector 可以簡化程序。
6、更加重要的是,試著將程序考慮為一組由類和對象表示的相互作用的概念,而不是一堆數據結構和一些可以撥弄的二進制
C/C++(C++內存管理,內聯函數,類型轉換,命名空間,string類)