C++学习过程分享

news2025/6/6 15:06:28

空指针:int *p = NULL;

  1. 空指针:指针变量指向内存中编号为0的空间;
  2. 用途:初始化指针变量
  3. 注意:空指针指向的内存不允许访问
  4. 注意:内存编号为0-255为系统占用空间,不允许用户访问

野指针:指针变量指向非法的内存空间

总结:空指针和野指针都不是我们申请的空间,因此不要访问

  1. const修饰指针 -- 常量指针 const int *p = &a;

特点:指针的指向可以修改,但是指针指向的值不能修改。

  1. const修饰常量 -- 指针常量 int * const p = &a;

特点:指针的指向不可以改,指针指向的值可以改

  1. count既修饰指针,也修饰常量

特点:指针的指向和指针指向的值都不可以改

#include <bits/stdc++.h>(万能头文件)

冒泡排序:

/* 冒泡排序 */

void bubbleSort(int nums[], int size) {

    // 外循环:未排序区间为 [0, i]

    for (int i = size - 1; i > 0; i--) {

        // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端

        for (int j = 0; j < i; j++) {

            if (nums[j] > nums[j + 1]) {

                int temp = nums[j];

                nums[j] = nums[j + 1];

                nums[j + 1] = temp;

            }

        }

    }

}

C语言动态内存分配(C++ string本质 结构体+字符串)

#include <stdio.h>

#include <stdlib.h> // 用于动态分配内存

#include <string.h>

struct Student {

    int id;        // 学号

    char *name;    // 字符指针,用于动态分配内存存储字符串

};

int main()

 {

    struct Student student1;

    // 给成员赋值

    student1.id = 1;

    // 动态分配内存

    student1.name = (char *)malloc(50 * sizeof(char)); // 分配 50 字节

    if (student1.name == NULL) {

        printf("内存分配失败\n");

        return -1;

    }

    strcpy(student1.name, "李四"); // 将字符串复制到动态分配的内存中

    // 打印结构体内容

    printf("学号: %d\n", student1.id);

    printf("姓名: %s\n", student1.name);

    // 释放内存

    free(student1.name);

    return 0;

}

C++结构体+字符串(string):

#include <iostream>

#include <string>

using namespace std;

struct student

{

string name;

int age;

int score;

};

int main()

{

student s;

s.age = 20;

s.name = "LYF";

s.score = 100;

cout << "姓名:" << s.name << " 年龄:" << s.age << " 分数:" << s.score << endl;

system("pause");

return 0;

}

变量值(数据多)传递多用指针(将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来,注意形参加上const,防止在函数中修改原变量)

Switch:

  1. 在 switch 的每个 case 块中,可以直接写多行代码,无需大括号 {}。
  2. 尽管在 case 中 不需要强制使用大括号,但在一些复杂的场景下,加上大括号可以提高代码的可读性或避免逻辑错误。
  3. 在 case 分支中如果需要定义局部变量,必须用大括号将分支语句包裹起来,否则会导致编译错误。
  4. 如果 case 分支里的逻辑比较复杂,包含嵌套的 if-else 或 for 循环等,建议使用大括号使代码结构更清晰。

system("cls");//清屏操作

内存4区域:

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

代码区:

  1. 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
  2. 代码区是只读的,使其只读的原因是防止程序意外修改了它的指令

全局区(data,bss):

  1. 存放全局变量和静态变量以及常量
  2. 常量区中存放const修饰的全局常量和字符串常量(“hello world”)const修饰局部变量(局部常量)不在全局区

栈区(Stack):

  1. 由编译器自动分配释放,存放函数的参数值,局部变量等
  2. 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区(Heap):

  1. 堆区数据由程序员管理开辟和释放
  2. 堆区数据利用new关键字进行开辟内存(变量:int *p = new int(10); 数组:int *arr = new int[10];)
  3. 堆区数据利用delete关键字进行释放内存(delete p; delete[] arr;)

引用:

  1. 作用:给变量起别名
  2. 语法:数据类型 &别名 = 原名
  3. 作用:使用引用传递,形参修饰实参:

void swapNum(int &a, int &b)

{

int temp;

temp = a;

a = b;

b = temp;

}

swapNum(a, b);

  1. 注意事项:引用必须初始化 && 引用在初始化后,不可改变
  2. 本质:引用的本质是一个指针常量
  3. 作用:const int &a;通常用来修饰形参值传递防止误操作

void printNum(const int &a, const int &b)

{

cout  << “a: ” << a << endl;

cout  << “b: ” << a << endl;

}

printNum(a, b);

函数高级:

  • 函数的默认参数:
  1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
  2. 函数声明和函数定义时参数只能有一处有默认参数
  • 占位参数(void func(int a = 10; int = 10);)
  • 函数重载
  1. 同一个作用域下
  2. 函数名称相同
  3. 函数参数类型不同 / 个数不同 / 顺序不同
  4. 注意事项:引用作为重载条件(可以):

void func(int &a)

{

cout << "int &a" << endl;

}

void func(const int &a)

{

cout << "const int &a" << endl;

}

func(a);

func(10);

  1. 注意事项:函数重载碰到函数默认参数(可能会产生二义性):

void func(int a, int = 10)

{

cout << "int a, int" << endl;

}

void func(int a)

{

cout << "int &a" << endl;

}

func(a);(错误)

func(a, 10);(正确)

类和对象:

C++面向对象的三大特性为:封装、继承、多态

C++认为万事万物皆为对象,对象上有其属性和行为

  • 封装
  1. 类中的属性和行为 我们统一称为成员
  2. 属性 成员属性 成员变量
  3. 行为 成员函数 成员方法

class Student(类)

{

public:

//属性

string name;

int number;

//行为

void printMessage()

{

cout << "姓名: " << name << "\t";

cout << "学号: " << number << endl;

}

void setName(string tName)

{

name = tName;

}

void setNumber(int tNumber)

{

number = tNumber;

}

};

  1. 访问权限:

公共权限public 成员 类内可以访问 类外可以访问

保护权限protected 成员 类内可以访问 类外不可以访问 继承中子类可以访问父类保护内容

私有权限private 成员 类内可以访问 类外不可以访问 继承中子类不可以访问父类私有内容

  1. class与struct的区别:默认权限不同(class默认权限私有pricate  struct默认权限公有public)
  2. 成员属性设为私有

作用:1.保护原有的成员属性不可外部更改 2.使用公共的方法读和写私有成员属性达到访问成员属性的目的3.可以检查通过方法给成员赋的值是否合理

  1. 应用1:判断两个立方体是否相等,全局函数判断(两个参数),class成员函数判断长宽高(一个参数)

应用2:点和圆的关系,本类中可以用另一个类作为本类的成员

  1. 类内变量命名规则(m_xxx) member变量 与全局变量或局部变量做区分
  2. 封装类:

.c文件中:Point::setX(int X){return m_X};Point是作用域

.h文件:#prajma once

#include <iostream>

  •  对象特性
  1. 对象的初始化和清理

- 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用的时候也会删除一些自己的信息数据保证安全。

- C++中的面向对象来源于生活,每个对象也都会有初始化设置以及对象销毁前的清理数据的设置。

  1. 构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题:

一个对象或者变量没有初始状态,对其使用后果是未知。(构造函数)

同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。(析构函数)

C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数:主要作用在于对对象销毁系统自动调用,执行一些清理工作。

  1. 构造函数语法:

类名() {}

构造函数,没有返回值也不写 void

函数名称与类名相同

构造函数可以有参数,因此可以发生重载

程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次

  1. 析构函数语法:

~类名() {}

析构函数,没有返回值也不写 void

函数名称与类名相同,在名称前加上符号 ~

析构函数不可以有参数,因此不可以发生重载

程序在对象销毁时会自动调用析构,无须手动调用,而且只会调用一次(例子:栈区)

  1. 构造函数的分类及调用

两种分类方式:

- 按参数分为:有参构造和无参构造(默认构造,注意:使用默认构造函数时不加括号 原因:Person a(); 编译器会认为是函数的声明而不是实例化对象)

- 按类型分为:普通构造和拷贝构造

例:

// 无参(默认)构造函数  

Person() {  

    cout << "无参构造函数!" << endl;  

}  

// 有参构造函数  

Person(int a) {  

    age = a;  

    cout << "有参构造函数!" << endl;  

}  

// 拷贝构造函数  

Person(const Person &p) {  

    age = p.age;  

    cout << "拷贝构造函数!" << endl;  

}  

三种调用方式:

括号法:

Person p1;     // 默认构造函数调用  

Person p2(10); // 有参构造函数  

Person p3(p2); // 拷贝构造函数

显示法:

Person p1;                // 默认构造  

Person p2 = Person(10);     // 有参构造  

Person p3 = Person(p2);     // 拷贝构造  

Person(10); // 匿名对象  特点:当前行执行结束后,系统会立即回收掉匿名对象  

Person(p3); //注意不要利用拷贝构造函数初始化匿名对象 编译器会认为 Person(p3) === Person p3;  

隐式转换法:

Person p4 = 10; // 相当于 写了 Person p4 = Person(10); 有参构造  

Person p5 = p4; // 拷贝构造  

  1. 拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况:

- 使用一个已经创建完毕的对象来初始化一个新对象

- 值传递的方式给函数参数传值:

void doWork(Person p)

{

}

void test02()

{

    Person p;

    doWork(p);

}

以值方式返回局部对象:

Person doWork2()

{

    Person p1;

    return p1;

}

void test03()

{

    Person p = doWork2();

}

  1. 4.2.4 构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数  

- 默认构造函数(无参,函数体为空)  

- 默认析构函数(无参,函数体为空)  

- 默认拷贝构造函数,对属性性进行值拷贝  

构造函数调用规则如下:  

- 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造  

- 如果用户定义拷贝构造函数,C++不再提供其他构造函数  

以下是图片中的文字提取结果:

---

  1. 深拷贝与浅拷贝  

深浅拷贝是面试经典问题,也是常见的一个坑  

- 浅拷贝:简单的赋值拷贝操作

- 深拷贝:在堆区重新申请空间,进行拷贝操作(如果成员变量里面有指针开辟的空间,就需要用深拷贝)

//自己实现拷贝构造函数 解决浅拷贝带来的问题

Person(const Person &p)

{

    cout << "Person 拷贝构造函数调用" << endl;

    m_Age = p.m_Age;

    //m_Height = p.m_Height; 编译器默认实现就是这行代码

    //深拷贝操作

    m_Height = new int(*p.m_Height);

}

栈释放:先进后出

  1. 初始列表

作用:

C++ 提供了初始化列表语法,用来初始化属性  

语法:  

构造函数():属性1(值1), 属性2(值2) ... {}

// 初始化列表初始化属性

Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c)

{

}

  1. 类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员。

例如:

class A {};

class B

{

    A a;

}

B类中有对象A作为成员,A为对象成员。

当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构的顺序与构造相反

  1. 静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。

静态成员分为:(通过对象访问、通过类名访问 Person::func();)

- 静态成员变量:

- - 所有对象共享同一份数据

- - 在编译阶段分配内存

- - 类内声明,类外初始化

- 静态成员函数:

- - 所有对象共享一个函数

- - 静态成员函数只能访问静态成员变量

  •  C++对象模型和this指针
  1. 成员变量和成员函数分开存储

- 在C++中,类内的成员变量和成员函数分开存储

- 只有非静态成员变量才属于类的对象上

- 空对象占用的字节数为1

  1. this指针概念

通过1我们知道在C++中成员变量和成员函数是分开存储的

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途:

- 当形参和成员变量同名时,可用this指针来区分

- 在类的非静态成员函数中返回对象本身,可使用 return *this(链式编程思想)

// 链式编程思想

p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);

cout << "p2的年龄为:" << p2.age << endl;

  1. 空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

void showPersonAge()

{

    // 报错原因是因为传入的指针是为NULL

    if (this == NULL)

    {

        return;

    }

    

    cout << "age = " << this->m_Age << endl;

}

void test01()

{

    Person * p = NULL;

    // p->showClassName();

    p->showPersonAge();

}

  1. const修饰成员函数

常函数:

- 成员函数后加const后我们称为这个函数为常函数

- 常函数内不可以修改成员属性

- 成员属性声明时加关键字mutable后,在常函数中依然可以修改

// this指针的本质 是指针常量 指针的指向是不可以修改的

// const Person * const this;

// 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改

void showPerson() const

{

    this->m_B = 100;

    // this->m_A = 100;

    // this = NULL;   // this指针不可以修改指针的指向的

}

int m_A;

mutable int m_B; // 特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable

常对象:

- 声明对象前加const称该对象为常对象

- 常对象只能调用常函数

void test02()

{

    const Person p; // 在对象前加const,变为常对象

    // p.m_A = 100;

    p.m_B = 100; // m_B是特殊值,在常对象下也可以修改

    // 常对象只能调用常函数

    p.showPerson();

    // p.func(); // 常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性

}

  •  友元

生活中你的家有客厅(Public),有你的卧室(Private)

客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去

但是呢,你也可以允许你的好闺蜜好基友进去。

在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术

友元的目的就是让一个函数或者类访问另一个类中私有成员

友元的关键字为 friend

友元的三种实现

- 全局函数做友元

// 建筑物类

class Building

{

    // goodGay全局函数是Building好朋友,可以访问Building中私有成员

    friend void goodGay(Building *building);

public:

    Building()

    {

        m_SittingRoom = "客厅";

        m_BedRoom = "卧室";

    }

private:

    string m_SittingRoom;  // 客厅

    string m_BedRoom;      // 卧室

};

- 类做友元

class Building

{

    // GoodGay类是本类的好朋友,可以访问本类中私有成员

    friend class GoodGay;

public:

    Building();

};

- 成员函数做友元

class Building

{

    // 告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员

    friend void GoodGay::visit();

public:

    Building();

};

  •  运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

  1. 加号运算符重载

作用:实现两个自定义数据类型相加的运算

自定义类 Person:

class Person  

{  

public:  

    int m_A;  

    int m_B;  

};

使用 Person 类的示例:

Person p1;  

p1.m_A = 10;  

p1.m_B = 10;

Person p2;  

p2.m_A = 10;  

p2.m_B = 10;  

Person p3 = p1 + p2;(此时不能实现对象相加)

- 通过成员函数实现自定义加法逻辑:

Person PersonAddPerson(Person &p)  

{  

    Person temp;  

    temp.m_A = this->m_A + p.m_A;  

    temp.m_B = this->m_B + p.m_B;  

    return temp;  

}

编译器给重载运算符定义了一个通用名称

- 通过成员函数重载 + 运算符:

Person operator+(Person &p)  

{  

    Person temp;  

    temp.m_A = this->m_A + p.m_A;  

    temp.m_B = this->m_B + p.m_B;  

    return temp;  

}

Person p3 = p1.operator+(p2);  // 简化后

Person p3 = p1 + p2;

- 通过全局函数重载 + 运算符

Person operator+(Person &p1, Person &p2)  

{  

    Person temp;  

    temp.m_A = p1.m_A + p2.m_A;  

    temp.m_B = p1.m_B + p2.m_B;  

    return temp;  

}

Person p3 = operator+(p1, p2);  // 简化后

Person p3 = p1 + p2;

  1. 函数调用运算符重载

- 函数调用运算符()也可以重载

- 由于其最后使用的方式非常特殊的应用,因此称为函数

- 函数调用有直接写法,非常灵活

class MyAdd

{

public:

int operator()(int num1, int num2)

{

return num1 + num2;

}

};

void test02()

{

MyAdd myadd;

int temp = myadd(5, 10);

cout << "temp = " << temp << endl;

//匿名函数对象

cout << MyAdd()(100, 100) << endl;

}

  •  继承
  1. 基本语法

- 减少重复代码

- 语法: class 子类: 继承方式 父类

- 子类 也称为 派生类

- 父类 也称为 基类

class BasePage

{

public:

void header()

{

cout << "首页、公开课、登录、注册...(公共头部)" << endl;

}

void footer()

{

cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;

}

void left()

{

cout << "Java、Python、C++、...(公共分类列表)" << endl;

}

};

class Java: public BasePage

{

public:

void content()

{

cout << "Java学科视频" << endl;

}

};

void test()

{

Java java;

java.header();

java.left();

java.footer();

java.content();

}

  1. 继承方式

  1. 继承中的对象模型

class Base

{

public:

    int m_A;

protected:

    int m_B;

private:

    int m_C; //私有成员只是在类内部可见,但是还是会继承下去

};

//公共继承

class Son : public Base //占用16个字节

{

public:

    int m_D;

};

  1. 构造和析构顺序

- 先构造父类,再构造子类,析构的顺序与构造的相反

  1. 继承同名成员处理方式

- 访问子类同名成员 直接访问即可

- 访问父类同名成员 需要加作用域

class Base

{

public:

Base()

{

m_A = 100;

}

int m_A;

};

class Son :public Base

{

public:

Son()

{

m_A = 200;

}

int m_A;

};

void test01()

{

Son s;

cout << "m_A = " << s.m_A << endl;

cout << "Base m_A = " << s.Base::m_A << endl;

}

  1. 同名静态函数处理

- 访问子类同名成员 直接访问即可

- 访问父类同名成员 需要加作用域

class Base

{

public:

static int m_A;

};

int Base::m_A = 100;

class Son :public Base

{

public:

static int m_A;

};

int Son::m_A = 200;

void test01()

{

Son s;

//通过对象的方式访问

cout << "Son m_A = " << s.m_A << endl;

cout << "Base m_A = " << s.Base::m_A << endl;

//通过类名的方式访问

cout << "Son m_A = " << Son::m_A << endl;

cout << "Base m_A = " << Son::Base::m_A << endl;

}

  1. 多继承语法

- 实际开发中不太建议使用多继承

- 加作用域

class Base1

{

public:

Base1()

{

m_A = 100;

}

int m_A;

};

class Base2

{

public:

Base2()

{

m_A = 200;

}

int m_A;

};

class Son :public Base1, public Base2

{

public:

Son()

{

m_B = 200;

}

int m_B;

};

void test01()

{

Son s;

cout << "Base1::m_A = " << s.Base1::m_A << endl;

cout << "Base2::m_A = " << s.Base2::m_A << endl;

}

  1. 菱形继承

- 虚继承(关键字virtual)

class Animal

{

public:

int m_Age;

};

class Sheep :virtual public Animal {};

class Tuo :virtual public Animal {};//指向同一块地址

class SheepTuo :public Sheep, public Tuo {};

void test01()

{

SheepTuo st;

st.Sheep::m_Age = 28;

st.Tuo::m_Age = 28;

st.m_Age = 28;

//两句cout等价

cout << "st.m_Age = " << st.m_Age << endl;

cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;

}

  •  多态
  1. 多态的基本语法

- 动态多态的满足条件

  - 有继承关系

  - 子类重写父类的虚函数

- 动态多态使用

  - 父类的指针或者引用 执行子类对象

class Animal

{

public:

virtual void speak()

{

cout << "动物在说话" << endl;

}

};

class Cat: public Animal

{

public:

void speak()

{

cout << "小猫在说话" << endl;

}

};

class Dog: public Animal

{

public:

void speak()

{

cout << "小狗在说话" << endl;

}

};

void do_speak(Animal &animal)

{

animal.speak();

}

void test01()

{

Cat cat;

do_speak(cat);

Dog dog;

do_speak(dog);

}

  1. 多态的原理刨析(虚函数表)
  2. 计算机类

- 多态的好处

  - 组织结构清晰

      - 可读性强

  - 对于前期和后期扩展以及维护性高

//利用多态实现计算器

class AbstractCalculator

{

public:

virtual ~AbstractCalculator()

{

}

virtual int Get_Result()

{

return 0;

}

int m_Num1;

int m_Num2;

};

class AddCalculator :public AbstractCalculator

{

public:

int Get_Result()

{

return m_Num1  + m_Num2;

}

};

class SubCalculator :public AbstractCalculator

{

public:

int Get_Result()

{

return m_Num1  - m_Num2;

}

};

class MulCalculator :public AbstractCalculator

{

public:

int Get_Result()

{

return m_Num1  * m_Num2;

}

};

void test02()

{

AbstractCalculator *abs =new AddCalculator;

abs->m_Num1 = 100;

abs->m_Num2 = 100;

cout << abs->m_Num1 << "+" << abs->m_Num2 << "=" << abs->Get_Result() << endl;

delete abs;

abs = new SubCalculator;

abs->m_Num1 = 100;

abs->m_Num2 = 100;

cout << abs->m_Num1 << "-" << abs->m_Num2 << "=" << abs->Get_Result() << endl;

delete abs;

abs = new MulCalculator;

abs->m_Num1 = 100;

abs->m_Num2 = 100;

cout << abs->m_Num1 << "*" << abs->m_Num2 << "=" << abs->Get_Result() << endl;

}

  1. 纯虚函数和抽象类

- 纯虚函数语法:virtual 返回值类型 函数名 (参数列表) =  0;

- 当类中有了纯虚函数,这个类也称为抽象类

class AbstractDrinking

{

public:

virtual void Boil() = 0;

virtual void Brew() = 0;

virtual void PutInCup() = 0;

virtual void PutSomething() = 0;

virtual ~AbstractDrinking()

{}

void MakeDrink()

{

Boil();

Brew();

PutInCup();

PutSomething();

}

};

class Coffee :public AbstractDrinking

{

public:

virtual void Boil()

{

cout << "煮农夫山泉" << endl;

}

virtual void Brew()

{

cout << "冲泡咖啡" << endl;

}

virtual void PutInCup()

{

cout << "倒入杯中" << endl;

}

virtual void PutSomething()

{

cout << "加入牛奶和糖" << endl;

}

};

class Tea :public AbstractDrinking

{

public:

virtual void Boil()

{

cout << "煮农夫山泉" << endl;

}

virtual void Brew()

{

cout << "冲泡茶叶" << endl;

}

virtual void PutInCup()

{

cout << "倒入杯中" << endl;

}

virtual void PutSomething()

{

cout << "加入柠檬" << endl;

}

};

void doWork(AbstractDrinking *abs)

{

abs->MakeDrink();

delete abs;

}

void test01()

{

AbstractDrinking *abs = new Coffee;

abs->MakeDrink();

delete abs;

}

  1. 虚析构和纯虚析构

虚析构和纯虚析构共性:

- 可以解决父类指针释放子类对象

- 都需要有具体的函数实现(需要有声明 也需要类似空实现

虚析构和纯虚析构的区别:

- 如果是纯虚析构,该类属于抽象类,无法实例化对象

class CPU

{

public:

virtual void calculate() = 0;

virtual ~CPU(){}

};

class GPU

{

public:

virtual void display() = 0;

virtual ~GPU(){}

};

class Memory

{

public:

virtual void storage() = 0;

virtual ~Memory(){}

};

class Computer

{

public:

Computer(CPU *cpu, GPU *gpu, Memory *mem)

{

m_cpu = cpu;

m_gpu = gpu;

m_mem = mem;

}

void Work()

{

m_cpu->calculate();

m_gpu->display();

m_mem->storage();

}

~Computer()

{

if(m_cpu != NULL)

{

delete m_cpu;

m_cpu = NULL;

}

if(m_gpu != NULL)

{

delete m_gpu;

m_gpu = NULL;

}

if(m_mem != NULL)

{

delete m_cpu;

m_mem = NULL;

}

}

private:

CPU *m_cpu;

GPU *m_gpu;

Memory *m_mem;

};

class IntelCPU :public CPU

{

public:

virtual void calculate()

{

cout << "Intel的CPU正在工作" << endl;

}

};

class IntelGPU :public GPU

{

public:

virtual void display()

{

cout << "Intel的GPU正在工作" << endl;

}

};

class IntelMemory :public Memory

{

public:

virtual void storage()

{

cout << "Intel的Memory正在工作" << endl;

}

};

class LenovoCPU :public CPU

{

public:

virtual void calculate()

{

cout << "Lenovo的CPU正在工作" << endl;

}

};

class LenovoGPU :public GPU

{

public:

virtual void display()

{

cout << "Lenovo的GPU正在工作" << endl;

}

};

class LenovoMemory :public Memory

{

public:

virtual void storage()

{

cout << "Lenovo的Memory正在工作" << endl;

}

};

void test01()

{

CPU *IntelCpu = new IntelCPU;

GPU *IntelGpu = new IntelGPU;

Memory *IntelMem = new IntelMemory;

Computer *computer1 = new Computer(IntelCpu, IntelGpu, IntelMem);

computer1 ->Work();

delete computer1;

cout << "----------------------------" << endl;

Computer *computer2 = new Computer(new LenovoCPU, new LenovoGPU, new LenovoMemory);

computer2->Work();

delete computer2;

cout << "----------------------------" << endl;

Computer *computer3 = new Computer(new IntelCPU, new IntelGPU, new LenovoMemory);

computer3->Work();

delete computer3;

}

  •  文件操作

写文件步骤如下:

1. 包含头文件

   #include <fstream>

2. 创建流对象

   ofstream ofs;

3. 打开文件

   ofs.open("文件路径", 打开方式);

4. 写数据

   ofs << "写入的数据";

5. 关闭文件

   ofs.close();

文件打开方式:

打开方式    解释

ios::in     为该文件而打开文件

ios::out    为写文件而打开文件

ios::ate    初始位置:文件尾

ios::app    添加方式打开文件

ios::trunc  如果文件存在则删除,再创建

ios::binary 二进制打开模式

注意:文件打开方式可以组合使用,利用 | 操作符

例如:用二进制方式打开文件  ios::binary || ios::out

void test01()

{

ofstream ofs;

ofs.open("test.txt", ios::out);

ofs << "姓名:张三" << endl;

ofs << "性别:男" << endl;

ofs << "年龄:18" << endl;

ofs.close();

}

读取文件与文件步骤相似,但是读取方式相比于较多

该文件读取步骤如下:

1. 包含头文件

#include <stream>

2. 创建流对象

ifstream ifs;

3. 打开文件并判断文件是否打开成功

ifs.open("文件路径",打开方式);

4. 读取数据

四种方式读取

5. 关闭文件

ifs.close();

void test01()

{

ifstream ifs;

ifs.open("test.txt", ios::in);

if(!ifs.is_open())

{

cout << "文件打开失败"  << endl;

return;

}

// 读数据

// 第一种

// char buf[1024] = {0};

// while(ifs >> buf)

// {

// cout << buf << endl;

// }

//第二种

// char buf[1024] = {0};

// if(ifs.getline(buf, sizeof(buf)))

// {

// cout << buf << endl;

// }

    //第三种

// string buf;

//

// while(getline(ifs,buf))

// {

// cout << buf << endl;

// }

//

//第四种

// char c;

// while((c = ifs.get()) != EOF)

// {

// cout << c;

// }

ifs.close();

}

  •  模板
  1. 模板的概念

- 模板就是通用的模具,大大提高复用性

  1. 函数模板

C++另一种编程思想称为泛型编程,主要利用的技术就是模板

C++提供两种模板机制:函数模板和类模板

- 函数模板语法:

函数模板作用:

建立一个通用函数,其函数返回类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

语法:

template<typename T>

函数声明或定义

解释:

template ---声明创建模板

typename ---表明其后面的符合是一种数据类型,可以用class代替

T --- 通用的数据类型,名称可以替换,通常为大写字母。

Example:

//函数模板

template<typename T>

void mySwap(T &a, T &b)

{

T temp = a;

a = b;

b = temp;

}

//1. 自动类型推导

//mySwap(a, b);

//2. 显示指定类型

mySwap<int>(a, b);

cout << "a =" << a << endl;

cout << "b =" << b << endl;

  1. 函数模板的注意事项
  1. 自动类型推导,必须推导出一致的数据类型T才可以使用。
  2. 模板必须要确定出T的数据类型,才可以使用。

  1. 普通函数与函数模板的区别

- 普通函数调用时可以发生自动类型转换(隐式类型转换)

   - 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

- 如果利用显示指定类型的方式,可以发生隐式类型转换

Example:

//普通函数

int My_Add01(int a, int b)

{

return a + b;

}

template<class T>

T My_Add02(T a, T b)

{

return a + b;

}

void test01()

{

int a = 10;

//int b = 20;

char c = 'c';

cout << My_Add01(a, c) << endl;

//自动类型推导

cout << My_Add02(a, c) << endl;//error

//给出T的数据类型

cout << My_Add02<int>(a, c) << endl;

}

  1. 普通函数与函数模板的调用规则

- 如果函数模板和普通模板都可以实现,优先调用普通函数

- 可以通过空模板参数列表来强制调用函数模板 myPrint<>(a, b);

- 函数模板也可以发生重载

- 如果函数模板可以更好的匹配,优先调用函数模板

  1. 模板的局限性

  1. 类模板

Example:

template<class NameType, class AgeType>

class Person

{

public:

Person(NameType name, AgeType age)

{

this->m_Name = name;

this->m_Age = age;

}

void Show_Person()

{

cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

}

NameType m_Name;

AgeType m_Age;

};

void test01()

{

Person<string, int>p1("孙悟空", 999);

p1.Show_Person();

}

  1. 类模板和函数模板的区别

类模板和函数模板的区别有两点:

- 类模板没有自动类型推导的使用方式

- 类模板在模板参数列表中可以有默认参数

example:

template<class NameType, class AgeType = int>

class Person {

public:

Person(NameType Name, AgeType Age)

{

this->m_Name = Name;

this->m_Age = Age;

}

void showPerson()

{

cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

}

NameType m_Name;

AgeType m_Age;

};

void test01()

{

Person<string>p1("猪八戒", 999);

p1.showPerson();

}

  1. 类模板中成员函数的创建时机

- 普通类中的成员函数一开始就可以创建

- 类模板中的成员函数在调用时才创建

  1. 类模板对象做函数参数

学习目标:

- 类模板实例化出的对象,向函数传参的方式

一共有三种传入方式:

- 指定传入类型 ---直接显示对象的数据类型

- 参数模板化   ---将对象中的参数变为模板进行传递

- 整个类模板化 ---将这个对象类型模板化进行传递

Example:

//类模板对象做函数参数

template<class T1, class T2>

class Person {

public:

Person(T1 name, T2 age)

{

this->m_Name = name;

this->m_Age = age;

}

void ShowPerson()

{

cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;

}

T1 m_Name;

T2 m_Age;

};

//1、指定传入类型

void printPerson1(Person<string, int>&p)

{

p.ShowPerson();

}

void test01()

{

Person<string, int>p("孙悟空", 100);

printPerson1(p);

}

//2、参数模板化

template<class T1, class T2>

void printPerson2(Person<T1, T2>&p)

{

p.ShowPerson();

cout << "T1的类型为: " << typeid(T1).name() << endl;

cout << "T2的类型为: " << typeid(T2).name() << endl;

}

void test02()

{

Person<string, int>p("猪八戒", 90);

printPerson2(p);

}

//3、整个类模板化

template<class T>

void printPerson3(T &p)

{

p.ShowPerson();

cout << "T1的类型为: " << typeid(T).name() << endl;

}

void test03()

{

Person<string , int>p("唐僧",30);

printPerson3(p);

}

  1. 类模板与继承

- 当子类继承的父类是一个模板时,子类在声明的时候,要指定出父类中T的类型。

- 如果不指定,编译器无法给子类分配内存

- 如果想要灵活制定出父类中T的类型,子类也需要变为类模板。


Example:

//类模板与继承

template<class T>

class Base

{

T m;

};

//class Son :public Base //错误的 必须要知道父类中T的类型,才能继承给子类

class Son :public Base<int>

{

};

void test01()

{

Son s1;

}

//如果想灵活指定父类中T的类型,子类也需要变成类模板

template<class T1, class T2>

class Son2 :public Base<T2>

{

public:

Son2()

{

cout << "T1的类型为:" << typeid(T1).name() << endl;

cout << "T2的类型为:" << typeid(T2).name() << endl;

}

T1 obj;

};

void test02()

{

Son2<int, char>S2;

}

  1. 类模板成员函数类外实现

- 能够掌握类模板中的成员函数类外实现

template<class T1, class T2>

class Person {

int YourVar;

public:

Person(T1 name, T2 age);

// {

// this->m_Name = name;

// this->m_Age = age;

// }

void ShowPerson();

// {

// cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

// }

T1 m_Name;

T2 m_Age;

};

//构造函数的类外实现

template<class T1, class T2>

Person<T1, T2>::Person(T1 name, T2 age)

{

this->m_Name = name;

this->m_Age = age;

}

//成员函数的类外实现

template<class T1, class T2>

void Person<T1, T2>:: ShowPerson()

{

cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

}

void test01()

{

Person<string, int>p("Tom", 20);

p.ShowPerson();

}

int main()

{

test01();

system("pause");

return 0;

}

  1. 类模板分文件编写(.hpp)

hpp:

#pragma once

#include <iostream>

using namespace std;

#include <string>

template<class T1, class T2>

class Person {

int YourVar;

public:

Person(T1 name, T2 age);

// {

// this->m_Name = name;

// this->m_Age = age;

// }

void ShowPerson();

// {

// cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

// }

T1 m_Name;

T2 m_Age;

};

//构造函数的类外实现

template<class T1, class T2>

Person<T1, T2>::Person(T1 name, T2 age)

{

this->m_Name = name;

this->m_Age = age;

}

//成员函数的类外实现

template<class T1, class T2>

void Person<T1, T2>:: ShowPerson()

{

cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;

}

Cpp:

#include <iostream>

using namespace std;

#include <string>

#include "Person.hpp"

  1. 类模板与友元

学习目标:

- 掌握类模板配合友元函数的类内类外实现

- 全局函数类内实现 - 直接在类内声明友元即可

- 全局函数类外实现 - 需要提前让编译器知道全局函数的存在
template<class T1,class T2>

class Person {

friend void printPerson(Person<T1,T2>p)

{

cout << "姓名: " << p.m_Name << "年龄:" << p.m_Age << endl;

}

public:

Person(T1 name, T2 age)

{

this ->m_Name = name;

this ->m_Age = age;

}

T1 m_Name;

T2 m_Age;

};

  •  STL初识
  1. STL的诞生

- 长久以来,软件界一直希望建立一种可重复利用的东西

- C++的面向对象和泛型编程的思想,目的就是复用性的提升

- 大多数情况下,数据结构与算法都未能有一套标准,导致被迫重复做大量重复工作

- 为了建立数据结构与算法的一套标准,诞生了STL

  1. STL的基本概念

- STL(模板标准库)

- STL从广义上分为:容器、算法、迭代器

- 容器和算法之间通过迭代器进行无缝连接

- STL几乎所有的代码都采用了模板类或者模板函数

  1. STL六大组件

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

- 容器:各种数据结构、如vector、list、deque、set、map等,用来存放数据。

- 算法:各种常用的算法、如sort、find、copy、for_each等。

- 迭代器:扮演了容器与算法之间的胶合剂。

- 仿函数:行为类似函数,可作为算法的某种策略。

- 适配器:一种来修饰容器或者仿函数或迭代器接口的东西。

- 空间配置器:负责空间的配置与管理。

  1. STL中容器、算法、迭代器

容器:置物之所也

STL容器就是将广泛运用的某些数据结构实现出来。

常用的数据结构:数组、链表、栈、队列、集合、映射等等。

这些容器分为序列式容器关联式容器两种:

序列式容器:强烈顺序的容器,序列容器中的每个元素均有固定的位置。

关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。

算法:问题之解决也

有限的步骤,解决逻辑数学上的问题,这门学科我们叫做算法(Algorithms)。

算法分为:质量算法和非质量算法。

质量算法:是指运算过程中特定区域内的元素的内容。例如排序、查找、删除等等。

非质量算法:是指在运算过程中不要求区域内的元素内容,例如查找、计数、遍历、寻找值等等。

送代器:容器和算法之间粘合剂

提供一种方法,使之能够将程序访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

每个容器都有自己专属的送代器。

送代器使用非常类似于指针,初学阶段我们可以先理解送代器为指针。

  1. Vector存放内置数据类型

Example:

#include <iostream>

using namespace std;

#include <vector>

#include <algorithm>

//vector容器存放内置数据类型

void myPrint(int val)

{

cout << val << endl;

}

void test01()

{

//创建一个vector容器、数组

vector<int> v;

//向容器中插入数据

v.push_back(10);

v.push_back(20);

v.push_back(30);

v.push_back(40);

//通过迭代器访问容器中的数据

// vector<int>::iterator itBegin = v.begin();

// vector<int>::iterator itEnd = v.end();

//

// while(itBegin != itEnd)

// {

// cout << *itBegin << endl;

// itBegin ++;

// }

//第二种for循环的遍历方式

// for(vector<int>::iterator it = v.begin();it != v.end();it ++)

// {

// cout << *it << endl;

// }

//第三种遍历方式 利用STL提供遍历算法

for_each(v.begin(), v.end(), myPrint);

}

int main()

{

test01();

system("pause");

return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2398877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言 — 动态内存管理

目录 1.malloc和free函数1.1 malloc函数1.2 free函数1.3 malloc函数的使用 2.calloc函数2.1 calloc函数2.2 calloc函数的使用 3.realloc函数3.1 realloc函数3.2 realloc函数的使用 4.动态内存管理笔试题4.1 笔试题&#xff08;1&#xff09;4.2 笔试题&#xff08;2&#xff09…

《TCP/IP 详解 卷1:协议》第5章:Internet协议

IPv4和IPv6头部 IP是TCP/IP协议族中的核心协议。所有TCP、UDP、ICMP和IGMP 数据都通过IP数据报传输。IP提供了一种尽力而为、无连接的数据报交付服务。 IP头部字段 IPv4 头部通常为 20 字节&#xff08;无选项时&#xff09;&#xff0c;而 IPv6 头部固定为 40 字节。IPv6 不…

学习STC51单片机26(芯片为STC89C52RCRC)

每日一言 真正的强者&#xff0c;不是没有眼泪&#xff0c;而是含着泪依然奔跑。 硬件&#xff1a;4G模块 这个是接线原理&#xff0c;我们也只要知道这个4根线的连接就好了&#xff0c;我们也是连接到USB转TTL的模块上 要插卡哈......... 随后我们下载一个叫做亿佰特的调试助…

Nginx详解(三):ngx_http_rewrite_module模块核心指令详解

概要&#xff1a; 在 Nginx 的众多功能模块中&#xff0c;ngx_http_rewrite_module是实现请求动态处理的核心组件&#xff0c;它通过一系列指令实现 URI 重写、条件判断、响应返回等功能。本文将以 CentOS 7.9 环境为例&#xff08;主机名www.a.com&#xff0c;IP 172.25.0.10…

【笔记】在 MSYS2(MINGW64)中正确安装 Poetry 的指南

#工作记录 在 MSYS2&#xff08;MINGW64&#xff09;中正确安装 Poetry 的指南 一、背景说明 在 MSYS2&#xff08;MINGW64&#xff09;环境中&#xff0c;即使已经安装了 pip&#xff0c;也不建议直接使用 pip install poetry 来安装 Poetry。 这是因为 MSYS2 使用自己的包…

IDEA项目推送到远程仓库

打开IDEA——>VCS——>Creat Git 选择项目 push提交到本地 创建远程仓库 复制地址 定义远程仓库 推送 推送成功

【后端架构师的发展路线】

后端架构师的发展路线是从基础开发到技术领导的系统性进阶过程&#xff0c;需融合技术深度、架构思维和业务洞察力。以下是基于行业实践的职业发展路径和关键能力模型&#xff1a; 一、职业发展阶梯‌ 初级工程师&#xff08;1-3年&#xff09;‌ 核心能力‌&#xff1a;掌…

matlab/simulink TLC语法基础练习实例

一、基本语法测试方法 1.新建一个脚本&#xff0c;保存扩展名为tlc,本例中是tst.tlc&#xff0c;设置当前工作路径为保存的tlc文件路径&#xff0c;在tlc文件里面输入下面的代码&#xff0c;然后保存&#xff1a; %warning test 2.在MATLAB的命令窗口输入&#xff1a; tlc …

蓝桥杯国赛训练 day1

目录 k倍区间 舞狮 交换瓶子 k倍区间 取模后算组合数就行 import java.util.HashMap; import java.util.Map; import java.util.Scanner;public class Main {static Scanner sc new Scanner(System.in);public static void main(String[] args) {solve();}public static vo…

ESP32之Linux编译环境搭建流程

背景&#xff1a;为了解决 “windows环境中编译ESP32代码速度慢” 的问题&#xff0c;现搭建一个Linux环境&#xff0c;让windows下的VScode连接到Linux环境&#xff0c;VSCode负责编辑代码&#xff0c;虚拟机用于编译代码。 目录 一、安装VMware 1.1 获取VMware安装包 1.2…

UE5打包项目设置Project Settings(打包widows exe安装包)

UE5打包项目Project Settings Edit-Project Settings- Packaging-Ini Section Denylist-Advanced 1&#xff1a;打包 2&#xff1a;高级设置 3&#xff1a;勾选创建压缩包 4&#xff1a;添加要打包地图Map的数量 5&#xff1a;选择要打包的地图Maps 6&#xff1a;Project-Bui…

便捷高效能源服务触手可及,能耗监测系统赋能智能建筑与智慧城市

在建筑行业迈向智能化、精细化管理的进程中&#xff0c;传统建筑管理模式因信息割裂、数据利用不足等问题&#xff0c;逐渐难以满足现代建筑复杂的运营需求。楼宇自控系统实现了建筑设备的智能调控&#xff0c;BIM技术则构建了建筑的三维数字化模型&#xff0c;当两者相遇&…

ISO18436-2 CATII级振动分析师能力矩阵

ISO18436-2021是当前针对针对分析师的一个标准&#xff0c;它对振动分析师的能力和知识体系做了4级分类&#xff0c;这里给出的是一家公司响应ISO18436的CATII级标准&#xff0c;做的一个专题培训的教学大纲。摘自&#xff1a; 【振動噪音產學技術聯盟】04/19-23 ISO 18436-2…

dvwa4——File Inclusion

LOW: 先随便点开一个文件&#xff0c;可以观察到url栏变成这样&#xff0c;说明?page是dvwa当前关卡用来加载文件的参数 http://10.24.8.35/DVWA/vulnerabilities/fi/?pagefile1.php 我们查看源码 &#xff0c;没有什么过滤&#xff0c;直接尝试访问其他文件 在url栏的pag…

Spring Boot养老院管理系统源码分享

概述 基于Spring Boot开发的养老院管理系统&#xff0c;该系统通过智能化管理模块&#xff0c;为养老机构提供高效运营解决方案。 主要内容 后台管理功能 系统后台功能完善&#xff0c;左侧导航栏涵盖首页、安全巡查管理、设备管理等模块。设备管理界面以表格形式清晰展示设…

go|context源码解析

文章目录 Context接口Deadline()Done()Err()Value() canceler接口ctxemptyCtxcancelCtxtimerCtxvalueCtx 基本使用cancelCtxvalueCtx 首先看一下源码对“context”的描述&#xff0c; When a Context is canceled, all Contexts derived from it are also canceled. 当一个Cont…

如何在PowerBI中使用Analyze in Excel

如何在PowerBI中使用Analyze in Excel 之前分享过如何使用DAXStudio将PowerBI与Excel连接 &#xff0c;今天介绍另外一个工具&#xff0c;也可以实现同样的功能&#xff0c;Analyze in Excel。 使用Analyze in Excel 第一步&#xff1a; 首先准备好一个PBIX文件&#xff0c…

大模型分布式训练笔记(基于accelerate+deepspeed分布式训练解决方案)

文章目录 一、分布式训练基础与环境配置&#xff08;1&#xff09;分布式训练简介&#xff08;2&#xff09;如何进行分布式训练&#xff08;3&#xff09;分布式训练环境配置 二、数据并行-原理与实战&#xff08;pytorch框架的nn.DataParallel&#xff09;1&#xff09;data …

鸿蒙UI开发——组件的自适应拉伸

1、概 述 针对常见的开发场景&#xff0c;ArkUI开发框架提供了非常多的自适应布局能力&#xff0c;这些布局可以独立使用&#xff0c;也可多种布局叠加使用。本文针对ArkUI提供的拉伸能力做简单讨论。 拉伸能力是指容器组件尺寸发生变化时&#xff0c;增加或减小的空间全部分…

鸿蒙仓颉语言开发教程:自定义弹窗

假期第一天&#xff0c;祝大家端午节快乐。昨天观看了时代旗舰尊界S800的发布&#xff0c;不得不感慨这车真好啊&#xff5e; 放假闲来无事&#xff0c;继续跟大家分享仓颉语言的开发教程&#xff0c;今天介绍一下自定义弹窗。 仓颉语言中的自定义弹窗和ArkTs类似&#xff0c…