C++对C的扩展

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)

重载规则:

  1. 函数名相同
  2. 参数个数不同,参数的类型不同,参数顺序不同,均可构成重载
  3. 返回值类型不同不能构成重载
void func(int a); 
void func(char a);
void func(char a, int b);
void func(int a, char b);
char func(int a); // 与第一个函数有冲突

有的函数虽然有返回值类型,但不参与表达式运算,作为一条单独的语句
匹配原则:

  1. 严格匹配,找到则调用
  2. 通过隐式转换寻求一个匹配,找到则调用
// 函数重载(静多态)
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. 规则

  1. 默认的排序,从右到左,不能跳跃
  2. 函数的声明和定义一体时,默认参数在定义(声明)处。声明在前,定义在后,默认参数在声明处
  3. 一个函数,不能既作重载,由作默认参数,当至少写一个参数时,系统无法确认时重载还是默认参数
// 实参的个数 + 默认参数的个数 >= 行参的个数
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. 规则

  1. 引用没有定义,是一种关系型声明。声明它和原有某一变量的关系。因此类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址
  2. 声明时必须初始化,一经声明,不可变更
  3. 可对引用再次引用。多次引用的结果是某一变量具有多个别名
  4. &符号前有数据类型时,是引用。其它皆为取地址。
 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. 引用提高

引用的本质是指针。

  1. 可以定义指针的引用,但不能定义引用的引用
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;
}
  1. 可以定义指针的指针(二级指针),但不能定义引用的指针
int &*p = &ra; // 不可以
int *&:指针的引用,合法
int &*:引用的指针,是不合法的
  1. 可以定义指针数组,但不能定义引用数组,可以定义数组引用
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;
}
  1. 常引用
    const引用。可以防止对象的值被随意修改。
    1. const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。
    2. 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;
  1. 尽可能使用const
    原因:
    1. 避免无意修改数据的编程错误
    2. 可以处理const和非const实参。否则将只能接受非const数据
    3. 可使函数能够正确的生成并使用临时变量(如果实参与引用参数不匹配,就会生成临时变量)

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[]用法

  1. 开辟单变量地址空间
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;
}
  1. 开辟数组空间
一维: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[]用法

  1. int *a = new int;
delete a; // 释放单个int的空间
  1. 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. 注意事项

  1. new/delete是关键字,效率高于malloc和free
  2. 配对使用,避免内存泄漏和多重释放
  3. 避免交叉使用,比如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. 使用方法

  1. 直接指定命名空间:Space::a = 5;
  2. 使用using + 命名空间 + 空间元素:using Space::a; a = 2000;
  3. 使用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++书写规范建议

  1. 在c++中几乎不需要用宏,用const或enum定义显式的常量,用inline避免函数调用额外的开销,用模板刻画一组函数或类型,用namespace避免命名冲突
  2. 不要在需要变量之前去声明,以保证能立即对他进行初始化
  3. 不要用malloc,用new运算会更好
  4. 避免使用void *、指针算术、联合和强制,多数情况下, 强制都是设计错误的指示器
  5. 尽量少用数组和C风格的字符串,标准库中的string和vector可以简化程序
  6. 更重要的是,试着将程序考虑为一组由类和对象表示的相互作用的概念,而不是一堆数据结构和一些可以拨弄的二进制。
  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qi妙代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值