七、C++的动态内存分配
C语言中对内存的操作:malloc/calloc/realloc/free。
C++语言中则提供了new和delete运算符来进行内存的动态分配。
1、new/delete:对单个变量进行内存分配/释放。
2、new[]/delete[]:对数组进行内存分配/释放。
#include <iostream>
using namespace std;
int main(){
//int* pi = (int*)malloc(sizeof(int));
//free(pi);
int* pi = new int;
*pi = 8;
cout << *pi << endl;
delete pi;
pi = NULL;
pi = new int[10];
for (int i = 0;i < 10;++i)
pi[i] = i;
for (int i = 0;i < 10;++i)
cout << pi[i] << ' ';
cout << endl;
delete[] pi;
pi = NULL;
pi = new int(1234);
cout << *pi << endl;
delete pi;
pi = NULL;
char buf[4] = {0x12,0x34,0x56,0x78};
pi = new(buf) int;//定位new
cout << hex << *pi << endl;
cout << (void*)pi << ' ' << (void*)buf << endl;
//delete pi;//此处不能delete,new的是栈的内存,不能delete
int (*p)[4] = new int[3][4];
delete[] p;
int (*q)[4][5] = new int[3][4][5];
delete[] q;
return 0;
}
八、C++的字符串
C没有字符串专门机制,用char型数组在末尾加’\0’的办法来处理字符串,C++也兼容这种“C字符串”机制。但在C++中则把字符串封装成了一种数据类型string来专门处理字符串,它提供了非常丰富的字符串处理功能。而且,string还提供了与“C字符串”相互转换的方法,使用非常灵活。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int main(){
string s1 ("hello");
string s2 = "world";
(s1 += " ") += s2;
cout << s1 << endl;
string s3 = s1;
cout << s3 << endl;
cout << (s1 == s3) << endl;
s3[0] = 'H';
cout << s3 << endl;
cout << (s1 > s3) << endl;
cout << s1.length() << endl;
cout << (strcasecmp(s1.c_str(),s3.c_str()) == 0) << endl;
FILE* fp = fopen("string.txt", "w");
//fwrite(&s3, 1, sizeof(s3), fp);
fwrite(s3.c_str(), sizeof(char), s3.length(), fp);
fclose(fp);
return 0;
}
九、引用
9.1、引用即别名。
int a = 20;
int& b = a;
b = 10;
cout << a << endl;//10
9.2、引用必须初始化。
int a;
int* p;
a = 20;
p = &a;
int& b;//ERROR
int& b = a;//OK
9.3、引用一旦初始化就不能再引用其他变量。
int a = 20, c = 30;
int& b = a;
b = c;//ERROR
9.4、引用的应用场景
(1)引用型参数
a、修改实参
b、避免拷贝,通过加const可以防止在函数中意外的修改实参的值,同时还可以接受拥有常属性的实参。
(2)引用型返回值
int b = 10;
int a = func(b);
func(b) = a;
#include <iostream>
using namespace std;
int g_data = 100;
int& foo(void){
return g_data;
}
int main(){
int data = foo();
cout << data << endl;//100
foo() = 200;
cout << g_data << endl;
foo() += 100;
++foo();
cout << g_data << endl;//301
return 0;
}
从一个函数中返回引用往往是为了将该函数的返回值作为左值使用。但是,一定要保证函数所返回的引用的目标在该函数返回以后依然有定义,否则将导致不确定的后果。
不要返回局部变量的引用,可以返回全局、静态、成员变量的引用,也可以返回引用型形参变量本身。
9.5、引用和指针
(1)引用的本质就是指针,很多场合下引用和指针可以互换。
(2)在C++层面上引用和指针存在以下不同:
A、指针是实体变量,但是引用不是实体变量。
int& a = b;
sizeof(a);//4
double& d = f;
sizeof(d);//8
B、指针可以不初始化,但是引用必须初始化。
C、指针的目标可以修改,但是引用的目标不能修改。
D、可以定义指针的指针,但是不能定义引用的指针。
int a;
int* p = &a;
int** pp = &p;//OK
int& r = a;
int&* pr = &r;//ERROR
E、可以定义指针的引用,但是不能定义引用的引用。
int a;
int* p = &a;
int*& q = p;//OK
int& r = a;
int&& s = r;//ERROR
F、可以定义指针的数组,但是不能定义引用的数组。
int a,b,c;
int* parr[] = {&a,&b,&c};//OK
int& rarr[] = {a,b,c};//ERROR
G、可以定义数组的引用。
int arr[] = {1,2,3};
int (&arr_ref)[3] = arr;//OK
十、显式类型转换运算符
C:目标类型变量 = (目标类型)源类型变量;
10.1、静态类型转换
static_cast<目标类型> (源类型变量)
如果在目标类型和源类型之间某一个方向上可以做隐式类型转换,那么在两个方向上都可以做静态类型转换。反之如果在两个方向上都不能做隐式类型转换,那么在任意一个方向上也不能做静态类型转换。
int* p1 = ...;
void* p2 = p1;
p1 = static_cast<int*> (p2);
如果存在从源类型到目标类型的自定义转换规则,那么也可以使用静态类型转换。
10.2、动态类型转换
dynamic_cast<目标类型> (源类型变量)
用在具有多态性的父子类指针或引用之间。
10.3、常类型转换
const_cast<目标类型> (源类型变量)
给一个拥有const属性的指针或引用去常:
const int a = 100;
const int* p1 = &a;
*p1 = 200;//ERROR
int* p2 = const_cast<int*> (p1);
*p2 = 200;//OK
#include <iostream>
using namespace std;
int main(){
const volatile int a = 100;
const volatile int* p1 = &a;
//*p1 = 200;
int* p2 = const_cast<int*> (p1);
*p2 = 200;
cout << *p2 << endl;//200
cout << a << endl;//200
//cout << 100 << endl;
return 0;
}
10.4、重解释类型转换
reinterpret_cast<目标类型> (源类型变量);
在不同类型的指针或引用之间做类型转换,以及在指针和整数之间做类型转换。
#include <iostream>
using namespace std;
int main(){
int i = 0x12345678;
char* p = reinterpret_cast<char*> (&i);
for (size_t i = 0;i < 4;++i)
cout << hex << (int)p[i] << ' ';
cout << endl;
float* q = reinterpret_cast<float*> (&i);
cout << *q << endl;
void* v = reinterpret_cast<void*> (i);
cout << v << endl;
return 0;
}
/*
运行结果:
78 56 34 12
5.69046e-28
0x12345678
*/
10.5、目标类型变量 = 目标类型(源类型变量);
int a = int(3.14);
十一、C++之父的建议
1、尽量少用宏,多用const、enum和inline。
#define PAI 3.14159
const double PAI = 3.14159;
#define ERROR_FILE -1
#define ERROR_MEM -2
enm{
ERROR_FILE = -1;
ERROR_MME = -2;
};
#define max(a,b) ((a) > (b) ? (a) : (b))
inline double max(double a,double b){
return a > b ? a : b;
}
2、变量随用随声明同时初始化。
3、少用malloc/free,多用new/delete。
4、少用C风格的强制类型转换,多用类型转换运算符。
5、少用C风格的字符串,多用string。
6、尽量多使用引用,少使用指针。
7、树立面向对象的编程思想。