C++开发学习笔记1

using的三种用法详解

  1. using声明(引入单个名称)

    using声明是将命名空间中某个名字单独引入到当前作用域。这使得我们在当前作用域下可以直接使用该名字而无需使用作用域限定符。

    1
    2
    using std::string;
    string s = "123";
  2. using指示(引入命名空间)

    using指示就是将一个命名空间中的所有名字全部引入到当前作用域(将命名空间在当前作用域展开)。可能会存在命名冲突的问题。

using namespace std; //我们常用的std命名空间展开

  1. 类型重定义(替代typedef)

    1
    using alias = typename;	//使用别名去替代原始类型(重命名)

    在C++11中,我们可以使用这样的语法来替代typedef的功能了。

    1
    2
    using ULL = unsigned long long;		//typedef unsigned long long ULL;
    using func = void(*)(int, int); //typedef void(*func)(int, int);

数据类型

  1. 整数类型

    1
    2
    3
    4
    std::cout << sizeof(short) << std::endl; //2字节
    std::cout << sizeof(int) << std::endl;//4字节
    std::cout << sizeof(long) << std::endl; //windows 4字节 / Linux 32位=4字节 64位=8字节 / MACOS=8字节
    std::cout << sizeof(long long) << std::endl; //8字节
  2. 浮点型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    std::cout << sizeof(float) << std::endl; // 4字节 单精度  7位有效数字
    float f1 = 12345.1234567;
    std::cout << f1 << std::endl; //12345.1

    float f1 = 12345.1234567;
    std::cout << f1 << std::endl; //12345.1

    float f1 = 123.1234567;
    std::cout << f1 << std::endl; //123.123

    float f1 = 1.1234567;
    std::cout << f1 << std::endl; //1.12346
    std::cout <<sizeof (double) << std::endl; // 8 字节 双精度 15-16 位有效数字
  3. 字符型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char c1 = 'a';//必须用单引号,并只能放一个字符
    std::cout << sizeof(char) << std::endl; // 1字节
    char c2 = 'assssssssq';
    std::cout << c2 << std::endl;//q

    //对应ASCII 码
    char c3 = 'a';
    std::cout << (int)c3 << std::endl;//97
    char c3 = 'A';
    std::cout << (int)c3 << std::endl;//65
  4. 字符串类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //C中的字符串
    char c3[] = "2324234324";
    std::cout << c3 << std::endl

    //C++中的字符串
    //需要加头文件 #include <string>
    //书写方式
    std::string str = "qqqqq";
    //如果想省略std,需要指定空间
    using namespace std;
    //在main中可书写成:
    string str = "qqqqq";
  5. 布尔类型

    1
    2
    3
    4
    5
    std::cout << sizeof(bool) << std::endl; //占用1字节
    bool flag = true;
    std::cout << flag << std::endl; // 1
    flag = false;
    std::cout << flag << std::endl; // 0

三元运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int a = 10;
int b = 20;
int c;
c = b > a ? b : a;
std::cout << c << std::endl; // c=20
////////////

int a = 10;
int b = 20;
(b > a ? b : a) = 100;
std::cout << b << std::endl; // 100
std::cout << a << std::endl; // 10
////////////

int a = 10;
int b = 20;
(b < a ? b : a) = 100;
std::cout << b << std::endl; // 20
std::cout << a << std::endl; // 100

随机数

1
2
3
4
5
6
7
8
// 系统生成随机数 这样生成的随机数,每次都是一样的,是伪随机数
int num = rand() % 100 + 1;

//添加随机数种子, 作用是利用当前系统时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
//这样生成的随机数,每次都不一样
int num = rand() % 100 + 1;
std::cout << num << std::endl;

goto

在程序中不建议使用 goto 语句,以免造成程序的混乱

数组

1
2
3
4
5
6
7
8
9
int arr[5] = {1, 2, 3, 4, 5};
// 查看数组的内存大小
std::cout << sizeof(arr) << std::endl;//20 int类型一个数字占4个字节,5个数字就占20个字节
//通过数组名查看数组地址
std::cout << &arr << std::endl; //0x7ffee5434b70
//通过数组名查看数组第一个元素地址 (与数组地址一致)
std::cout << &arr[0] << std::endl; //0x7ffee5434b70
//通过数组名查看数组第二个元素地址
std::cout << &arr[1] << std::endl; //0x7ffee5434b7
  1. 冒泡排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int arr[9] = {3, 1, 6, 7, 4, 5, 9, 0, 2};
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]) - 1; ++i) {
for (int j = 0; j < sizeof(arr) / sizeof(arr[0]) - i - 1; ++j) {
if (arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}

for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i) {
std::cout << arr[i] << std::endl;
}

  1. const修饰指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int a = 10;
    int b = 20;
    //const修饰的是指针,指针指向可以修改,指针指向的值不可以修改
    const int * p2 = &a;
    p2 = &b; //正确
    *p2 = 100; //错误

    //const修饰的是常量,指针指向不可以修改,指针指向的值可以修改
    int * const p2 = &a;
    p2 = &b; //错误
    *p2 = 100; //正确

    //const既修饰指针又修饰常量
    const int * const p3 = &a;
    p3 = &b; //错误
    *p3 = 100; //错误
  2. 指针和数组

    1
    2
    3
    4
    5
    6
    //根据指针获取数据
    int arr[9] = {3, 1, 6, 7, 4, 5, 9, 0, 2};
    int *p = arr;
    std::cout << *p << std::endl;//3
    p++;
    std::cout << *p << std::endl;//1
  3. 指针和函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    }
    void swap2(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    }

    ////////////

    int a = 10;
    int b = 20;
    //值传递无法改变实参
    swap(a, b);
    std::cout << "a = " << a << std::endl; // 10
    std::cout << "b = " << b << std::endl;// 20

    //地址传递可法改变实参
    swap2(&a, &b);
    std::cout << "2a = " << a << std::endl;// 20
    std::cout << "2b = " << b << std::endl;// 10

结构体

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型

(相当java中的实体类、DTO、VO等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Student{
string name;
int age;
float score;
};

//赋值方式1
struct Student s1;
s1.name = "admin";
s1.age = 12;
s1.score = 45.2;

//赋值方式2
struct Student s1 = {"admin", 12, 34.5};

//赋值方式3
struct Student {
string name;
int age;
float score;
}s3;
s3.name = "admin";
s3.age = 12;
s3.score = 45.2;
  1. 结构体数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    struct Student {
    string name;
    int age;
    float score;
    };

    //方式1
    struct Student stuArray[3] = {
    {"张三", 12, 23.4},
    {"王五", 12, 23.4},
    {"马六", 12, 23.4}
    };

    //方式2
    struct Student stuArray[3];
    stuArray[0] = {"张三", 12, 23.4};
    stuArray[1] = {"王五", 12, 23.4};
    stuArray[2] = {"马六", 12, 23.4};

    //方式3
    struct Student stuArray[3];
    stuArray[0].name = "张三";
    stuArray[0].age = 12;
    stuArray[0].score = 23.9;

    stuArray[1].name = "王五";
    stuArray[1].age = 12;
    stuArray[1].score = 23.9;

    stuArray[2].name = "马六";
    stuArray[2].age = 12;
    stuArray[2].score = 23.9;
  2. 结构体指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Student {
    string name;
    int age;
    float score;
    };
    struct Student s1 = {"张三", 45, 34.5};
    struct Student *t = &s1;
    std::cout << t->name << std::endl;
    //设置值
    t->age = 28;
    std::cout << t->age << std::endl; //28
  3. 结构体嵌套结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //学生结构体
    struct Student {
    string name;
    int age;
    float score;
    };
    struct teacher {
    int id;
    string name;
    int age;
    struct Student stu;
    };

    teacher t1;
    t1.id = 1;
    t1.name = "老王";
    t1.age = 36;
    t1.stu.age = 16;
    t1.stu.name = "小李";
    t1.stu.score = 79;
  4. 结构体做函数参数

    将函数中的形参改为指针,可以减少内存空间,而且不会复制一个新的副本出来

  5. 结构体中使用const

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct Student {
    string name;
    int age;
    float score;
    };

    //加入const后,一旦有修改的操作就会报错,可以防止我们的误操作
    void printStudent(const Student *stu){
    stu->score = 100; // 此处会报错
    }
  6. 调用系统服务

    system(“pause”); // 任意键继续

    system(“cls”); //清屏

C++ 内存分区模型

  • 代码区:存放函数体的二进制代码,由操作系统管理
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束由操作系统回收

四区的意义:不同的区域存放的数据,赋予不同的生命周期,给我们更大的灵活编译

  1. 程序运行前

    再程序编译以后,生成exe可执行文件,未执行该程序前分两个区域

    • 代码区

      存放CPU执行的机械指令

      代码区是共享的,目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

      代码区是只读的,原因是防止程序意外修改它的指令

    • 全局区

      全局变量和静态变量存放在这里

      还包含了常量区,字符串常量和其他常量存放在这里

      该区域的数据在程序结束后有操作系统释放

    • C++中在程序运行之前分为全局区和代码区
    • 代码区特点是共享和只读
    • 全局区中存放全局变量、静态变量、常量
    • 常量区中存放const修饰的全局变量和字符串常量
  2. 程序运行后

    • 栈区

      由编译器自动分配释放,存放函数的参数值,局部变量等

      注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自定释放

    • 堆区

      由程序员分配释放,若程序员不释放,程序结束时,由操作系统回收

      在C++中主要利用new在堆中开辟内存

  3. new 操作符

    利用new 在堆区开辟内存

    由程序员手动开辟,手动释放,释放利用关键字:delete

    利用new 创建的数据,会返回该数据对应的类型的指针

    释放数组的时候,要加[]才可以

引用

作用:给变量起别名

语法:数据类型 别名 = 原名

1
2
3
4
5
6
7
8
9
10
11
//引用的基本类型
int a = 10;
//创建引用
int &b = a;
std::cout << a << std::endl; //10
std::cout << b << std::endl;//10

b = 100;
std::cout << a << std::endl; //100
std::cout << b << std::endl;//100
//a和b使用的是同一块内存
  • 注意事项

    • 引用必须初始化
    • 引用在初始化后,不可以改变
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //引用的基本类型
    int a = 10;
    //创建引用,引用必须初始化
    int &b; //错误的

    >>>>>>>>>>>>>>>>

    //引用的基本类型
    int a = 10;
    int c = 10;
    //创建引用
    int &b = a;
    //引用在初始化后,不可以改变
    &b = c;//错误
  • 引用做函数参数

    • 函数中的值传递,会改变形参的值,不会改变实参的值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      int a = 10;
      int c = 20;
      yesyt(a,c);
      std::cout << "main a = " << a << std::endl;
      std::cout << "main c = " << c << std::endl

      //函数
      void yesyt(int a,int c){
      int temp = a;
      a = c;
      c = temp;
      std::cout << "yesyt a = " << a << std::endl;
      std::cout << "yesyt c = " << c << std::endl;
      }
      //输出
      yesyt a = 20
      yesyt c = 10
      main a = 10
      main c = 20
    • 地址传递会改变形参和实参的值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      int a = 10;
      int c = 20;
      yesyt2(&a,&c);
      std::cout << "main a = " << a << std::endl;
      std::cout << "main c = " << c << std::endl;

      //函数
      void yesyt2(int *a,int *c){
      int temp = *a;
      *a = *c;
      *c = temp;
      std::cout << "yesyt a = " << *a << std::endl;
      std::cout << "yesyt c = " << *c << std::endl;
      }

      //输出
      yesyt2 a = 20
      yesyt2 c = 10
      main a = 20
      main c = 10
    • 引用传递会改变形参和实参的值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      int a = 10;
      int c = 20;
      yesyt3(a,c);
      std::cout << "main a = " << a << std::endl;
      std::cout << "main c = " << c << std::endl;

      //函数
      void yesyt3(int &a,int &c){
      int temp = a;
      a = c;
      c = temp;
      std::cout << "yesyt3 a = " << a << std::endl;
      std::cout << "yesyt3 c = " << c << std::endl;
      }

      //输出
      yesyt3 a = 20
      yesyt3 c = 10
      main a = 20
      main c = 10

      解析:函数 yesyt3 中 &a是实参 a的别名, &b是实参 b的别名,综合上述知道,更改别名的值,原名的值也会改变。

    • 引用做函数的返回值

      作用:引用是可以作为函数的返回值存在

      注意:不要返回局部变量引用 (局部变量存放在栈区,栈区数据在执行完成以后自动释放)

      用法:函数调用作为左值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //返回引用
      int& yesyt4(){
      static int d = 120;
      return d;
      }

      int &f = yesyt4();
      std::cout << "main1 f = " << f << std::endl; //120

      yesyt4() = 100; //如果函数返回的值是引用,这个函数调用可以作为左值。(此处等于 a=100)
      std::cout << "main2 f = " << f << std::endl;//100 (因为&f为a的别名,a的值变了,&f也就变了)
  • 引用的本质

    本质:引用的本质在C++内部实现是一个指针常量。所有的指针操作编译器帮我们做了

    指针常量:指针的指向不可修改,指针指向的值可以修改

1
2
3
4
5
6
7
8
9
10
int a = 10;
//自动转换为 int* const ref = &a; 指针常量是指针指向不可修改,也说明为什么引用不可更改
int& ref = a;
ref = 20;//内部发现ref是引用,自动帮我们转换为:*ref = 20;
func(a);

//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
ref = 100; //ref是引用,转换为 *ref = 100;
}
  • 常量引用

    作用:常量引用主要用来修饰形参,防止误操作

    函数形参列表中,可以加const修饰形参,防止形参改变实参

    1
    2
    3
    4
    5
    6
    int a = 10;
    func(a);

    void func(const int& ref){
    ref = 100; //此处报错
    }

函数提高

  1. 函数默认参数

在C++中,函数的形参列表中的形参是可以默认值的

语法:返回值类型 函数名(参数 = 默认值){}

示例:

1
2
3
4
5
6
7
8
9
int a = 10;
int b = 10;
func(a,b);

void func(int a,int b,int c = 14){
std::cout << "func a = " << a << std::endl;
std::cout << "func b = " << b << std::endl;
std::cout << "func c = " << c << std::endl;
}
1
2
3
4
5
6
void func(int a = 10,int b= 10);
//此处报错,因为声明和实现只能有一个有默认参数
void func(int a = 10,int b= 10){
std::cout << "func a = " << a << std::endl;
std::cout << "func b = " << b << std::endl;
}

  1. 函数占位参数

    C++中函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置

    语法:返回值类型 函数名(数据类型){}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //第二个int就是占位参数,调用是必须传int值(现在还没有用到)
    //占位参数也可以有默认参数
    void func(int a,int){
    std::cout << "func a = " << a << std::endl;
    }

    int a = 10;
    int b = 10;
    func(a,b);
  2. 函数重载

  • 函数重载概述

    作用:函数名可以相同,提高复用性

    条件:

    • 同一作用域下
    • 函数名相同
    • 参数不同或者个数不同或者顺序不同

    注意:函数的返回值不可以作为函数重载条件

  • 函数重载注意事项

    • 引用作为重载条件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // 下面两个函数是可以编译通过的
      void func(int &a){
      std::cout << "func a = " << a << std::endl;
      }

      void func(const int &a){
      std::cout << "func a = " << a << std::endl;
      }

      int a = 10;
      func(a); //调用不加const的函数,因为a是一个变量
      //int &a = 10 是不合法的

      func(10);//调用加const的函数
      //const int &a = 10 是合法的

    • 函数重载碰到函数默认值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      void func(int a){
      std::cout << "func a = " << a << std::endl;
      }

      void func(int a,int b = 10){
      std::cout << "func a = " << a << std::endl;
      }

      int a = 10;
      func(a);//此处报错
      func(a,10);//不会报错,但会走带默认参数的函数

      //当函数重载碰到默认参数,会出现二义性,应尽量避免这种情况

-------------本文结束感谢您的阅读-------------