1. 类型增强
1.1. 类型检查更严格
把一个const类型的指针赋给非const类型的指针。c语言可以通过,但是c++不可以
int main()
{
const int a = 100;
int b = a;
const int *pa = &a;
int *pb = pa; // ERROR
return 0;
}
1.2. 布尔类型
c语言的逻辑真假用0和非0表示,而c++有布尔类型
int main()
{
bool flag = true;
if (flag)
printf("true type\n");
printf("bool size = %lu\n", sizeof(bool)); // bool size = 1
return 0;
}
1.3. 真正的枚举(enum)
c语言中枚举本质是整型,枚举变量可以用任意整型赋值。而c++中枚举变量,只能用被枚举出来的元素初始化
enum season {SPR, SUM, AUT, WIN};
int main() {
enum season s = SUM;
s = 0; // ERROR
return 0;
}
1.4. 表达式的值可被赋值
c语言中表达式通常不能作为左值,而c++中某些表达式是可以赋值的
int main()
{
int a, b = 5;
(a = b) = 10;
cout << "a = " << a << " b = " << b << endl; // a = 10 b = 5
(a < b ? a : b) = 200;
cout << "a = " << a << " b = " << b << endl; // a = 10 b = 200
return 0;
}
2. 输入与输出
2.1. cin和cout
cin和cout是c++的标准输入流和输出流。在头文件iostream中定义的
cin cout是类对象,scanf和printf是函数
int main()
{
char name[30]; // 是不安全的,输入超过30个字符,会挂掉
cin >> name; // >> 流输入运算符
cout << "name =" << name << endl; // 首先把"name ="流进cout,再把name流入cout中,最后把endl流入cout中
// 等价于
// cout << "name =";
// cout << name;
// cout << endl;
return 0;
}
int main()
{
string name; // 安全,所有用字符数组解决的问题,都用string
cin >> name;
cout << name.max_size() << endl;
return 0;
}
2.2. 格式化
2.2.1. 设置域宽及位数
对于实型,cout默认输出六位有效数据。setprecision(2)可以设置有效位数,setprecision(n)<<setiosflags(ios::fixed)合用,可以设置小数点右侧的位数
int main5()
{
int a = 12345;
float b = 1234.5678;
// float b = 4.56789000;
// printf("%-8d--", a); // 12345 --
// printf("%10.2f\n", b); // 1234.57
// printf("%f", b); // 1234.567749
cout << "xxxxxxxxxxx" << endl;
// cout << setiosflags(ios::right) << setw(8) << a << endl; // 12345
cout << b << endl; // 1234.57 4.56789
// cout << setw(10) << b << endl; // 1234.57
cout << setw(10) << setprecision(2) << setiosflags(ios::fixed) << b << endl; // 1234.57
return 0;
}
2.2.2. 按进制输出
输出十进制,十六进制,八进制。默认输出十进制的数据
int main()
{
int i = 123;
cout << dec << i << endl; // 123
cout << oct << i << endl; // 173
cout << hex << i << endl; // 7b
cout << setbase(16) << i << endl; // 7b
return 0;
}
2.2.3. 设置填充字符
可以设置域宽的同时,设置左右对齐及填充字符
int main()
{
cout << setw(10) << 1234 << endl; // 1234
cout << setw(10) << setfill('0') << 1234 << endl; // 0000001234
cout << setw(10) << setfill('0') << setiosflags(ios::left) << 1234 << endl; // 1234000000
cout << setw(10) << setfill('-') << setiosflags(ios::right) << 1234 << endl; // ------1234
return 0;
}
3. 函数重载
3.1. 重载规则与调用匹配(overload & match)
重载规则:
- 函数名相同
- 参数个数不同,参数的类型不同,参数顺序不同,均可构成重载
- 返回值类型不同不能构成重载
void func(int a);
void func(char a);
void func(char a, int b);
void func(int a, char b);
char func(int a); // 与第一个函数有冲突
有的函数虽然有返回值类型,但不参与表达式运算,作为一条单独的语句
匹配原则:
- 严格匹配,找到则调用
- 通过隐式转换寻求一个匹配,找到则调用
// 函数重载(静多态)
void print(double a)
{
cout << a << endl;
}
void print(int a)
{
cout << a << endl;
}
int main()
{
print(1); // 1
print(1.1); // 1.1
print('a'); // 97
print(1.11f); // 1.11
return 0;
}
注:c++允许int到long、double,double到int、float的隐式类型转换,遇到这种情形,则会引起二义性(ambiguous);解决方法:在调用时重载
3.2. 重载底层实现(name mangling)
c++利用name mangling倾轧技术修改函数名,区分参数不同的同名函数。
实现原理:用v-c-i-f-l-d表示void char int float long double及其饮用
void func(char a); // func_c(char a)
void func(char a, int b, double c);
// func_cid(char a, int b, double c);
3.3. extern “C”
name mangling发生在两个阶段,.cpp编译阶段和.h声明阶段。只要两个阶段同时进行,才能匹配调用
mystring.h
extern "C"
{
int myStrlen(char *str);
}
mystring.cpp
int myStrlen(char *str)
// #include "mystring.h"
int myStrlen(char *str)
{
int len = 0;
whilie(*str++)
len++;
return len;
}
main.cpp
#include <iostream>
#include "mystring.h"
using namespace std;
int main()
{
char *p = "china";
int len;
len = myStrlen(p); // 5
return 0;
}
C++完全兼容C语言,完全兼容C的类库。由.c文件的类库文件中的函数名,并没有发生name mangling行为,而在包含.c文件对应的.h文件时,.h会发生name mangling行为,因此会发生在链接的时候报错
C++为了避免上述错误,重载了关键字extern,只需要,要避免name mangling的函数前加extern “C”,如有多个,则extern “C” { }
4. 操作符重载(operator overload)
<<符号在C语言中是位操作符中的左移运算符。在C++中用作流插入运算符,这种一个字符多种用处的现象叫做重载。
C语言没有开放重载机制,C++提供了重载机制。可以为自定义数据类型重载运算符,实现构造数据类型也可以像基本数据类型一样的运算特性
struct Complex
{
float real;
float image;
};
Complex operator+(Complex a, Complex b)
{
Complex c;
c.real = a.real + b.real;
c.image = a.image + b.image;
return c;
}
int main()
{
Complex aa = {1, 2}, bb = {2, 3};
Complex cc = aa + bb;
cout << "cc = " << "(" << cc.real << "," << cc.image << ")" << endl; // cc = (3,5)
return 0;
}
全局操作符+号用于实现将两个自定义结构体类型相加,本质是函数的调用
当然Complex operator+(Complex a, Complex b)也可以定义成Complex add(Complex a, Complex b),这样就只能Complex sum = add(aa, bb),而不能实现Complex cc = aa + bb;
5. 默认参数(default parameters)
函数在调用时,行参从实参那里取得值。对于多次调用同一函数同一实参时,C++给形参设置默认值,就不用从实参那里取值了
5.1. 示例
5.1.1. 单个参数
void weatherCast(char *w = "sunny")
{
time_t t = time(0); // 1970:00:00:00
char tmp[64];
strftime(tmp, sizeof(tmp), "%Y/%m/%d %X %A ", localtime(&t));
cout << tmp << "today is weather " << w << endl; // 2024/06/26 23:26:43 Wednesday today is weahter sunny
}
int main()
{
weatherCast();
weatherCast("rainy"); // 2024/06/26 23:27:04 Wednesday today is weahter rainy
return 0;
}
int volume(int l, int w, int h = 5)
{
return l * w * h;
}
int main()
{
cout << volume(1, 2) << endl; // 10
return 0;
}
5.1.2. 多个参数
// 定义和声明一体,默认参数定义和声明一体
// 声明在前,定义在后。默认参数在声明而不在定义
void func(int a, int b = 10);
int main()
{
func(1, 10); // 对同一函数,不要既实现重载,又有默认参数
return 0;
}
void func(int a, int b) {}
5.2. 规则
- 默认的排序,从右到左,不能跳跃
- 函数的声明和定义一体时,默认参数在定义(声明)处。声明在前,定义在后,默认参数在声明处
- 一个函数,不能既作重载,由作默认参数,当至少写一个参数时,系统无法确认时重载还是默认参数
// 实参的个数 + 默认参数的个数 >= 行参的个数
int volume(int l, int w, int h = 5)
{
return l * w * h;
}
int main()
{
cout << volume(1, 2) << endl; // 10
return 0;
}
6. 引用
6.1. 概念
变量名本身是一段内存的引用,即别名。引用,是为已有变量起一个别名
int main()
{
int a;
int &b = a;
}
6.2. 规则
- 引用没有定义,是一种关系型声明。声明它和原有某一变量的关系。因此类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址
- 声明时必须初始化,一经声明,不可变更
- 可对引用再次引用。多次引用的结果是某一变量具有多个别名
- &符号前有数据类型时,是引用。其它皆为取地址。
int main()
{
int a = 500; // 变量名,实质是一段内存空间的别名
// *(int *)0x12345678 = 500;
int &ra = a; // ra是a的引用
// int &ra; // 引用是一种声明关系,声明时必须初始化
printf("sizeof(a) = %d, sizeof(ra) = %d\n", sizeof(a), sizeof(ra)); // 4 4
printf("&a = %p, &a = %p\n", &a, &ra); // &a = 0x16bc82dd8, &a = 0x16bc82dd8
a = 100;
printf("a = %d, ra = %d\n", a, ra); // a = 100, ra = 100
ra = 2000;
printf("a = %d, ra = %d\n", a, ra); // a = 2000, ra = 2000
int b = 200;
ra = b; // 赋值:允许
// int &ra = a; // 声明:不允许
printf("a = %d, ra = %d\n", a, ra); // a = 200, ra = 200
int &rr = ra;
printf("rr = %d, ra = %d\n", rr, ra); // rr = 200, ra = 200
int &rrr = rr;
printf("rrr = %d, ra = %d\n", rrr, rr); // rrr = 200, rr = 200
return 0;
}
6.3. 应用
C++很少使用独立变量的引用,如果使用某一变量,就直接使用他的原名,没有必要使用他的别名
值作函数参数
void swap(int a, int b); // 无法实现两数据的交换
void swap(int &ra, int &rb); // 开辟了两个指针空间实现交换
引用作函数参数
void swap(int &ra, int &rb)
{
ra ^= rb;
rb ^= ra;
ra ^= rb;
}
int main6()
{
int a = 5;
int b = 4;
swap(a, b);
cout << a << " " << b << endl; // 4 5
return 0;
}
C++引入引用后,可以用引用解决的问题,避免了用指针来解决
6.4. 引用提高
引用的本质是指针。
- 可以定义指针的引用,但不能定义引用的引用
int main()
{
int *p;
int **pp = &p; // 可以
int *&rp = p;
int a;
int &ra = a;
// int &&rra = ra; // 不可以
return 0;
}
案例
void swap(char **p, char **q)
{
char *t = *p;
*p = *q;
*q = t;
}
// 平级内解决问题,不开辟多余的空间
// 引用的本质,是对指针的再次包装。指针是有引用,不应该有引用的指针
void swap(char *&p, char *&q)
{
char *t = p;
p = q;
q = t;
}
int main()
{
int a = 5;
int b = 4;
swap(a, b);
cout << a << " " << b << endl; // 4 5
char *p = "china";
char *q = "america";
cout << "p = " << p << " q = " << q << endl;
// swap(&p, &q);
swap(p, q);
cout << "p = " << p << " q = " << q << endl;
return 0;
}
- 可以定义指针的指针(二级指针),但不能定义引用的指针
int &*p = &ra; // 不可以
int *&:指针的引用,合法
int &*:引用的指针,是不合法的
- 可以定义指针数组,但不能定义引用数组,可以定义数组引用
int main()
{
int x, y, z;
int *p[] = {&x, &y, &z}; // or
// int &rp[] = {&x, &y, &z}; // error rp & int& int &*
int arr[] = {1, 2, 3, 4, 5}; // int[5] int[5]&
// int *&arr = arr; // error
int(&rarr)[5] = arr; // ok
return 0;
}
- 常引用
const引用。可以防止对象的值被随意修改。- const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。
- const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。
常引用原理:const引用的目的:禁止通过修改引用值来改变被引用的对象。
void func(const int a) {}
int main()
{
const int a = 100;
const int *p = &a;
const int &ra = a;
int d = 200;
const double &rd = d; // ok
const int &rb = 100; // ok
func(200 + a); // ok
return 0;
}
int main()
{
int a = 200;
int &ra = a;
const double &rd = a; // double temp = a; const double &rd = temp;
a = 400;
cout << "a = " << a << endl; // a = 200 a = 400
cout << "ra = " << ra << endl; // ra = 200 ra = 400
cout << "rd = " << rd << endl; // rd = 200
cout << "&a = " << &a << endl; // &a = 0x16f94add8
cout << "&ra = " << &ra << endl; // &ra = 0x16f94add8
cout << "&rd = " << &rd << endl; // &rd = 0x16f94adc0
// rd = 200; // error
return 0;
}
const引用使用相关类型对象初始化时发生了如下的过程
int temp = val;
const int &ref = temp;
如果ref不是const的,那么改变ref的值,修改的是temp,而不是val。
int i = 5;
const int &ref = i + 5;
// 此时产生了与表达式等值的无名临时变量
// 此时的引用是对无名临时变量的引用,因此不能修改
cout << ref << endl;
- 尽可能使用const
原因:- 避免无意修改数据的编程错误
- 可以处理const和非const实参。否则将只能接受非const数据
- 可使函数能够正确的生成并使用临时变量(如果实参与引用参数不匹配,就会生成临时变量)
6.5. 引用的本质浅析
引用的本质是指针,是对常指针type * const p 的再次包装
char &rc == *pc double &rd == *pd
struct TypeP
{
char *p;
};
struct TypeC
{
char c;
};
struct TypeR
{
char &r; // 把引用单列出来,不与具体的对象发生关系
};
int main()
{
printf("%d %d %d\n", sizeof(TypeP), sizeof(TypeC), sizeof(TypeR)); // 4 1 4
return 0;
}
7. new/delete Operator
7.1. new/new[]用法
- 开辟单变量地址空间
int main()
{
// int *p = (int *)malloc(sizeof(int)); // c语言中
// int *p = static_cast<int *>(malloc(sizeof(int))); // C++
int *p = new int; // int *p = new int(200);
*p = 200;
cout << *p << endl; // 200
string *ps = new string; // string *ps = new string("china");
*ps = "china";
cout << *ps << endl; // china
return 0;
}
int main()
{
struct Stu
{
int age;
string name;
};
Stu *pstu = new Stu{10, "wyb"};
cout << pstu->age << endl; // 10
cout << pstu->name << endl; // wyb
return 0;
}
- 开辟数组空间
一维:int *a = new int[100]{0}; 开辟一个大小为100的整形数组空间
int **p = new int *[5]{NULL};
二维:int (*a)[6] = new int[5][6];
三位:int (*a)[5][6] = new int[3][5][6];
四维及以上...
7.2. delete/delete[]用法
- int *a = new int;
delete a; // 释放单个int的空间
- int *a = new int[5];
delete []a; // 释放int数组空间
7.3. 关于返回值
int main()
{
// c版本
int *p = (int *)malloc(100);
if (p == NULL)
return -1;
//C++ 内存申请失败会抛出异常
try {
int *p = new int[10];
} catch(const std::bad_alloc e) {
return -1;
}
// c++内存申请失败不抛出异常
int *q = new (std::nothrow) int[100];
if (q == NULL)
return -1;
return 0;
}
7.4. 注意事项
- new/delete是关键字,效率高于malloc和free
- 配对使用,避免内存泄漏和多重释放
- 避免交叉使用,比如malloc申请的空间去delete,new出的空间被free
7.5.更进一步
new/delete关键字重点用在类对象的申请与释放。申请时会调用构造器完成初始化,释放的时候,会调用析构器完成内存的清理。
8. 内联函数(inline function)
8.1. 内联
C语言中的宏的特点是内嵌到调用代码中,避免了函数调用的开销。但是宏函数的处理发生在预处理阶段,缺失了语法检测和可能带来的语意差错
8.2. 语法
C++提供inline关键字,实现了真正的内嵌
inline变成了编译器的一种建议
宏函数 VS inline关键字
// 优点:内嵌代码,避免压栈与出栈的开销
// 缺点:代码替换,易使生成代码体积变大,易产生逻辑错误,无类型检查
#define SQR(x) ((x) * (x))
// 优点:高度抽象,避免重复开发,类型检查
// 缺点:压栈与出栈,带来开销
inline int sqr(int x)
{
return x * x;
}
int main()
{
int i = 0;
while(i < 5)
{
cout << sqr(i++) << endl; // 0 1 4 9 16
}
return 0;
}
8.3. 评价
优点:避免调用时的额外开销(出入栈操作)
代价:由于内联函数的函数体在代码段中会出现多个“副本”,因此会增加代码段的空间
本质:以牺牲代码空间为代价,提高程序的运行时间效率
使用场景:函数体很小,且被频繁调用
9. 类型强转(type case)
9.1. 静态类型转换
- 语法格式
static_cast<目标类型>(标识符)
对于隐式类型可以转化的
- 转化规则
在一个方向上可以作隐式转换,在另外一个方向上可以作静态转换
int main()
{
float a = 5.6;
int b = 5;
b = static_cast<int>(a);
a = static_cast<float>(b);
void *p;
int *q;
p = q;
// q = p; // error
q = static_cast<int *>(p);
int x = 10;
int y = 3;
float z = static_cast<float>(x) / y;
cout << z << endl;
char *pc = static_cast<char *>(malloc(100));
return 0;
}
9.2. 重解释类型转换
- 语法格式
reinterpret_case<目标类型>(标识符)
对于无隐式的类型转换
- 转化规则
将数据以二进制存在形式的重新解释,在双方向上都不可以隐式类型转换的,则需要重解释类型转化
char *p; int *q;
p = q;
q = p;
int main()
{
// char *p;
// int *q;
// p = reinterpret_cast<char *>(q);
int a[5] = {1, 2, 3, 4, 5};
int *p = reinterpret_cast<int *>((reinterpret_cast<int>(a) + 1));
cout << hex << *p << endl;
return 0;
}
9.3. (脱)常类型转换
- 语法格式:
const_cast<目标类型>(标识符) // 目标类类型只能是指针或引用
脱常
- 语法规则
用来移除对象的常量性。使用const_cast去除const限定的目的:是为了函数能够接受这个实际参数
应用场景1:
void func1(const int &v) // const对引用的一种扩展
{
// v = 100; // error
}
void func2(int &v)
{
cout << v << endl; // 10
}
int main()
{
// const一定不可以改
const int a = 10;
func1(10);
func2(const_cast<int &>(a)); // const_cast只能应用于指针引用
int &ra = const_cast<int &>(a);
ra = 200;
cout << "a = " << a << " ra = " << ra << endl; // 10 200
cout << "&a = " << &a << " &ra = " << &ra << endl; // 0x16dd7edd8 0x16dd7edd8
int *pi = const_cast<int *>(&a);
*pi = 200;
cout << "a = " << a << " *pi = " << *pi << endl; // 10 200
cout << "&a = " << &a << " pi = " << pi << endl; // 0x16dd7edd8 0x16dd7edd8
struct A
{
int data;
};
const A ad = {10};
A *pA = const_cast<A *>(&ad);
pA->data = 200;
cout << pA->data << endl; // 200
return 0;
}
可以改变const自定义类的成员变量,但对于内置数据类型,却表现未定义行为
- const常变量补充:
c++中const定义的变量称为常变量。变量的形式,常量的作用,用作常量,常用于取代#define宏常量
#define N 200; // 宏,在预处理阶段发生了替换
// const永远不会发生改变
int main()
{
const int a = 200; // 编译阶段发生了替换
int b = 300;
int c = a + b; // int c = N + b
return 0;
}
9.4. 动态类型转换
- 语法格式
dynamic_cast<目标类型>(标识符)
用于多态中的父子类之间的强制转化
10. 命名空间(namespace scope)
10.1. 为什么要引入namesapce
命名空间为了大型项目开发而引入的一种避免命名冲突的机制。比如:在一个大型项目中,要用到多家软件开发商提供的类库。在事先没有约定的情况下,两套类库可能存在同名的函数或是全局变量冲突。项目越大,冲突的可能性就会越高
10.2. 默认NameSpace(Global & Function)
Global scope是一个程序中最大的scope。也是引起命名冲突的根源,Global scope是无名的命名空间
int v = 55;
// :: 作用域运算符,前面要命名空间
void func3()
{
cout << "void func3" << endl;
}
int main()
{
int *p = &v;
int v = 5;
cout << v << endl; // 5
cout << *p << endl; // 55
cout << ::v << endl; // 55
func3(); // void func3 ::func3()没有意义,因为不能在函数内定义函数
return 0;
}
10.3. 语法规则
NameSpace是对全局Global scope区域的再次划分
10.3.1. 声明
命名空间的声明及namespace中可以包含的内容
namespace NAMESPACE
{
全局变量 int a;
数据类型 struct Stu{};
函数 void func();
其他命名空间 namespace
}
10.3.2. 使用方法
- 直接指定命名空间:Space::a = 5;
- 使用using + 命名空间 + 空间元素:using Space::a; a = 2000;
- 使用using + namespace + 命名空间:using namespace Space;
using namespace std;
namespace MySpace
{
int val = 5;
int x,y,z;
}
int main()
{
// MySpace::val = 200;
// cout<<MySpace::val;
// using MySpace::x;
// x = 100;
// cout<<x<<endl;
using namespace MySpace;
val = 1;
x = 2;
cout<<val<<x<<endl;
return 0;
}
类比std::cout / using std::cout using / namespace std;
无可避免的冲突
namespace Space
{
int x;
int y;
}
namespace Other
{
int x;
int y;
}
int main()
{
{
using namespace Space;
x = 10;
y = 20;
}
{
using namespace Other;
x = 100;
y = 200;
}
int x;
cout << x << endl;
return 0;
}
10.3.3. 支持嵌套
using namespace std;
namespace MySpace
{
int x = 1;
int y = 2;
namespace Other {
int m = 3;
int n = 4;
}
}
int main()
{
using namespace MySpace::Other;
cout<<m<<n<<endl;
return 0;
}
10.3.4. 协作开发
同名命名空间自动合并,对于一个命名空间中的类,要包含声明和实现
a.h
#ifndef A_H
#define A_H
namespace XX {
class A
{
public:
A();
};
}
#endif // A_H
a.cpp
#include "a.h"
using namespace XXX
{
A::A()
{
}
}
b,h
#ifndef B_H
#define B_H
namespace XX
{
class B
{
public:
B();
};
}
}
#endif // B_H
b.cpp
#include "b.h"
namespace XX {
B::B()
{
}
}
main.cpp
#include "a.h"
#include "b.h"
using namespace std;
using namespace XX;
int main()
{
A a;
B b;
return 0;
}
11. 系统string类
除了使用字符数组来处理字符串以外,c++引入了字符串类型,可以定义字符串变量。
11.1. 定义及初始化
int main()
{
// string str;
// string str("china");
string str = "china";
str = "good";
// string str2(str);
string str2;
str2 = str;
cout << str << endl;
cout << str2 << endl;
return 0;
}
11.2. 类型大小
int main()
{
// string类中包装了char *
string str;
cout << sizeof(string) << endl; // 4
cout << "sizeof(str) = " << sizeof(str) << endl; // 4
return 0;
}
11.3. 常用运算
11.3.1. 赋值
string str3 = str2;
11.3.2. 加法
string combine = str + str2;
11.3.3. 关系
string s1 = "abcedf";
string s2 = "123456";
if(s1 > s2)
cout << "s1 > s2" << endl;
else
cout << "s1 < s2" << endl;
string s3 = s1 - s2;
cout << s3 << endl;
11.4. 常见的成员函数
11.4.1. 下标操作
char & operator[](int n);
11.4.2. 求串大小
int size();
11.4.3. 返回c串
char *c_str();
11.4.4. 查找
int find(char c, int pos = 0);
int find(char * s, int pos = 0);
// 返回下标值,没有找到返回-1,默认从0下标开找
11.4.5. 删除
string &erase(int idx = 0, int n = npos);
// 作用是删除从idx开始,往后数n位的字符串
11.4.6. 交换swap
void swap(string &s2);
11.5. string类型数组
int main()
{
string sArray[10] = {
"0",
"1",
"22",
"333",
"4444",
"55555",
"666666",
"7777777",
"88888888",
"999999999",
};
for (int i = 0; i < 10; i++)
{
cout << sArray[i] << endl;
}
return 0;
}
string数组是高效的,如果用二维数组来存入字符串数组,容易空间浪费,此时列数是由最长的字符串决定的。如果用二级指针申请堆空间,依据大小申请相应的空间,虽然解决了内存浪费的问题,但是操作麻烦。用string数组存储字符串数组,效率又高又灵活
12. C++书写规范建议
- 在c++中几乎不需要用宏,用const或enum定义显式的常量,用inline避免函数调用额外的开销,用模板刻画一组函数或类型,用namespace避免命名冲突
- 不要在需要变量之前去声明,以保证能立即对他进行初始化
- 不要用malloc,用new运算会更好
- 避免使用void *、指针算术、联合和强制,多数情况下, 强制都是设计错误的指示器
- 尽量少用数组和C风格的字符串,标准库中的string和vector可以简化程序
- 更重要的是,试着将程序考虑为一组由类和对象表示的相互作用的概念,而不是一堆数据结构和一些可以拨弄的二进制。