内存管理
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、更加重要的是,试着将程序考虑为一组由类和对象表示的相互作用的概念,而不是一堆数据结构和一些可以拨弄的二进制