C++进修——C++核心编程

内存分区模型

  C++程序在执行时,将内存大方向划分为4个区域

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

  内存4区意义

  • 不同的区域存放不同的数据,赋予不同的生命周期,给我们更大的灵活编程

程序运行前

  在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

  代码区

    存放CPU执行的机器指令

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

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

  全局区

    全局变量和静态变量存放在此

    全局区还包括了常量区,字符串常量和其他常量也存放在此

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

示例:

int g_a = 10;
int g_b = 10;const int c_g_a = 10;
const int c_g_b = 10;int main(){//局部变量不在全局区int a = 10;int b = 10;cout << (int)&a << endl;cout << (int)&a << endl;cout << (int)&g_a << endl;cout << (int)&g_b << endl;static int s_a = 10;static int s_b = 10;cout << (int)&s_a << endl;cout << (int)&s_b << endl;cout << (int)&"hello world" << endl;cout << (int)&"hello world1" << endl;cout << (int)&c_g_a << endl;cout << (int)&c_g_b << endl;//局部常量不在全局区中const int c_l_a = 10;const int c_l_b = 10;cout << (int)&c_l_a << endl;cout << (int)&c_l_b << endl;system("pause");return 0;
}

程序运行后

  栈区

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

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

示例:

int * func(){int a = 10;return &a;
}int main(){int *p = func();cout << *p << endl;cout << *p << endl;system("pause");return 0;
}

  注意:

    x64函数的调用通常使用寄存器来传递参数和返回值,如果想要达到x86的效果,可以在两次输出之前输入system("pause")

  堆区

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

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

示例:

int* func() {int* a = new int(10);return a;
}int main() {int* p = func();cout << *p << endl;cout << *p << endl;system("pause");return 0;
}

new操作符

  C++中利用new操作符在堆区开辟数据

  堆区开辟的数,由程序员手动开辟,手动释放,释放利用操作符delete

  语法:new 数据类型

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

基本语法:

int* func() {int* a = new int(10);return a;
}int main() {int* p = func();cout << *p << endl;cout << *p << endl;delete p;system("pause");return 0;
}

示例:

int main() {int* arr = new int[10];for (int i = 0; i < 10; i++) {arr[i] = i + 100;}for (int i = 0; i < 10; i++) {cout << arr[i] << endl;}delete[] arr;system("pause");return 0;
}

引用

引用的基本使用

  作用:给变量起别名

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

示例:

int main(){int a = 10;int &b = a;cout << a << endl;cout << b << endl;b = 100;cout << a << endl;cout << b << endl;system("pause");return 0;
}

引用注意事项

  • 引用必须初始化
  • 引用在初始化后,不可以改变

示例:

int main(){int a = 10;int b = 20;//错误,引用必须初始化//int &c;//一旦初始化后,就不可以改变int &c = a;//这是赋值操作,不是更改引用	c = b;cout << a << endl;cout << b << endl;cout << c << endl;system("pause");return 0;
}

引用做函数参数

  作用:函数传参时,可以利用引用的技术让形参修饰实参
  优点:可以简化指针修改实参

示例:

void mySwap01(int a, int b) {int temp = a;a = b;b = temp;
}void mySwap02(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}void mySwap03(int& a, int& b) {int temp = a;a = b;b = temp;
}int main() {int a = 10;int b = 20;mySwap01(a, b);cout << a << endl;cout << b << endl;mySwap02(&a, &b);cout << a << endl;cout << b << endl;mySwap03(a, b);cout << a << endl;cout << b << endl;system("pause");return 0;
}

引用做函数返回值

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

  注意:不要返回局部变量引用

  用法:函数调用作为左值

示例:

int& test01(){int a = 10;return a;
}int& test02(){static int a = 20;return a;
}int main(){int& ref = test01();cout << ref << endl;cout << ref << endl;int& ref2 = test02();cout << ref2 << endl;cout << ref2 << endl;test02() = 1000;cout << ref2 << endl;cout << ref2 << endl;system("pause");return 0;
}

引用的本质

  本质:引用的本质在c++内部实现是一个指针常量

示例:

void func(int& ref){ref = 100;
}int main(){int a = 10;int& ref = a;ref = 20;cout << a << endl;cout << ref << endl;func(a);system("pause");return 0;
}

常量引用

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

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

示例:

void showValue(const int& v){//v += 10;cout << v << endl;
}int main(){//引用本身需要一个合法的内存空间,因此运行错误//int& ref = 10;//加入const就不会报错const int& ref = 10;cout << ref << endl;int a = 10;showValue(a);system("pause");return 0;
}

函数提高

函数默认参数

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

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

示例:

int func(int a,int b = 10,int c = 10){return a + b + c;
}//如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//如果函数声明有默认值,函数实现的时候就不能有默认参数int func2(int a = 10,int b = 10);
int func2(int a,int b){return a + b;
}int main(){cout << func(20,20) << endl;cout << func(100) << endl;system("pause");return 0;
}

函数占位参数

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

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

  在现阶段函数的占位参数存在意义不大,但是后面会用到该技术

示例:

void func(int a,int){cout << "func" << endl;
}int main(){//占位符必须填补func(10,10);system("pause");return 0;
}

函数重载

函数重载概述

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

  函数重载满足条件

  • 同一个作用于下
  • 函数名称相同
  • 函数参数类型不同 或者 个数不同 或者 顺序不同

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

示例:

void func(){cout << "func" << endl;
}
void func(int a){cout << "int a" << endl;
}void func(double a){cout << "double a" << endl;
}void func(int a,double b){cout << "double b" << endl;
}void func(double a,int b){cout << "int b" << endl;
}//函数返回值不可以作为函数重载条件
/*int func(double a,int b){cout << "double a" << endl;
}*/int main(){func();func(10);func(3.14);func(10,3.14);func(3.14,10);system("pause");return 0;
}

函数重载注意事项

  • 引用作为重载条件
  • 函数重载碰到函数默认参数

示例:

void func(int &a){cout << "&a" << endl;
}void func(const int &a){cout << "const" << endl;
}void func2(int a,int b = 10){cout << "a,b = 10" << endl;
}void func2(int a){cout << "a" << endl;
}int main(){int a = 10;//调用无constfunc(a);//调用有constfunc(10);//碰到默认参数产生歧义,需要避免//func2(10);system("pause");return 0;
}

类和对象

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

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

  例如

    人可以作为对象,属性有姓名、年龄等,行为有走、跑等

    车作为对象,属性有轮胎、方向盘等,行为有载人、放音乐等

    具有相同性质的对象,可以抽象称为

封装

封装的意义

  封装是C++面向对象三大特性之一

  封装的意义:

    将属性和行为作为一个整体,表现生活中的事物

    将属性和行为加以权限控制

  封装的意义一

    在设计类的时候,属性和行为写在一起,表现事物

  语法class 类名{访问权限:属性/行为};

示例:设计一个圆类,求圆的周长

const double pi = 3.14;class Circle{
public:int m_r;double calculate(){return 2 * pi * m_r;}
};int main(){Circle c1;c1.m_r = 10;cout << c1.calculate() << endl;system("pause");return 0;
}

  封装意义二

    类在设计时,可以把属性和行为放在不同的权限下,加以控制

  访问权限有三种:

    1. public 公共权限

    2. protected 保护权限

    3. private 私有权限

示例:

class Person{
public:string m_Name;protected:string m_Car;private:int m_Password;public:void func(){m_Name = "张三";m_Car = "汽车";m_Password = 123456;}
};int main(){Person p;p.m_Name = "李四";//在类外访问不到//p.m_Car = "aodi";//在类外访问不到//p1.m_Password = 123;p.func();system("pause");return 0;
}

struct 和 class 区别

  在 C++ 中 struct 和 class 唯一的区别就在于默认的访问权限不同

  区别:

  • struct 默认权限为公共
  • class 默认权限为私有

示例:

class C1{int m_A;
};struct C2{int m_A;
};int main(){C1 c1;c1.m_A = 10;C2 c2;c2.m_A = 10;system("pause");return 0;
}

成员属性设置为私有

  优点1:将所有成员属性设置为私有,可以自己控制读写权限

  优点2:对于写权限,可以检测数据的有效性

示例:

class Person{
public:void SetName(string name){m_Name = name;}string GetName(){return m_Name;}int GetAge(){return m_Age;}void SetAge(int age){m_Age = age;}void SetIdol(string idol){m_Idol = idol;}private:string m_Name;int m_Age = 18;string m_Idol;
};int main(){Person p;p.SetName("张三");cout << p.GetName() << endl;cout << p.GetAge() << endl;p.SetIdol("小明");system("pause");return 0;
}

对象的初始化和清理

  C++中的每个对象都会有初始设置以及对象销毁前的清理数据的设置

构造函数和析构函数

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

    一个对象或变量没有初始状态,对其使用后果是未知

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

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

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

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

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

  构造函数语法类名(){}

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

    函数名称和类名相同

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

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

  析构函数语法~类名(){}

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

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

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

    程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次

示例:

class Person{
public:Person(){cout << "构造函数" << endl;}~Person(){cout << "析构函数" << endl;}
};void Test(){Person p;
}int main(){Test();system("pause");return 0;
}

构造函数的分类和调用

  两种分类方式:

    按参数分为:有参构造和无参构造

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

  三种调用方式:

    括号法

    显示法

    隐式转换发

示例:

class Person{
public://无参(默认)构造函数Person(){cout << "无参构造函数" << endl;}//有参构造函数Person(int a){age = a;cout << "有参构造函数" << endl;}//拷贝构造函数Person(const Person& p){age = p.age;cout << "拷贝构造函数" << endl;}//析构函数~Person(){cout << "析构函数" << endl;}public:int age;
};void test01(){Person p;
}void test02(){//括号法(常用)Person p1(10);//注意:调用无参构造函数不能加括号,如果加了编译器认为是一个函数声明//Person p2();//显式法Person p2 = Person(10);Person p3 = Person(p2);//单独写就是匿名对象,当前行结束,马上析构//Person(10);//隐式转换法Person p4 = 10;Person p5 = p4;//注意:不能利用拷贝构造函数,初始化匿名对象,编译器认为是对象声明//Person p5(p4);
}int main(){test01();test02();system("pause");return 0;
}

拷贝构造函数调用时机

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

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

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

    以值方式返回局部对象

示例:

class Person{
public:Person(){cout << "默认构造函数" << endl;mAge = 0;}Person(int age){cout << "有参构造函数" << endl;mAge = age;}Person(const Person& p){cout << "拷贝构造函数" << endl;mAge = p.mAge;}~Person(){cout << "析构函数" << endl;}int mAge;
};void test01(){Person p1(20);Person p2(p1);cout << p2.mAge << endl;
}void doWork(Person p){}void test02(){Person p;doWork(p);
}Person doWork2(){Person p1;return p1;
}void test03(){Person p = doWork2();
}int main(){test01();test02();test03();system("pause");return 0;}

构造函数调用规则

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

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

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

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

  构造函数调用规则如下:

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

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

示例:

class Person{
public:Person(){cout << "默认构造函数" << endl;}Person(int a){age = a;cout << "有参构造函数" << endl;}Person(Person& p){m_Age = p.m_Age;cout << "拷贝构造函数" << endl;}~Person(){cout << "析构函数" << endl;}int m_Age;
};void test01(){Person p;p.m_Age = 18;Person p2(p);cout << p2.m_Age << endl;
}void test02(){Person p;Person p2(18);Person p3(p2);cout << p3.m_Age << endl;
}int main(){test01();test02();system("pause");return 0;
}

深拷贝与浅拷贝

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

  深拷贝:在堆区重新申请空间、进行拷贝工作

class Person{
public:Person(){cout << "无参构造函数" << endl;}Person(int age,int height){cout << "有参构造函数" << endl;m_Age = age;m_hight = new int(height);}Person(const Person& p){cout << "拷贝构造函数" << endl;m_Age = p.m_Age;//深拷贝操作m_height = new int(*p.m_height);}~Person(){cout << "析构函数" << endl;if(m_height != NULL){delete m_height;}}public:int age;int* m_height;
};void test01(){Person p1(10,100);Person p2(p1);cout << p1.m_Age << *p1.m_height << endl;cout << p2.m_Age << *p2.m_height << endl;
}int main(){test01();system("pause");return 0;
}

  如果属性在堆区开辟,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题(堆区重复释放)

初始化列表

  作用

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

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

示例:

class Person{
public:// 传统方式初始化/*Person(int a,int b,int c){m_A = a;m_B = b;m_C = c;}*///初始化列表初始化属性Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){}int m_A;int m_B;int m_C;
};void test01(){//Person p(10,20,30);Person p(30,20,10);cout << p.m_A << endl;cout << p.m_B << endl;cout << p.m_C << endl;
}int main(){test01();system("pause");return 0;
}

类对象作为类成员

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

例如:

class A{}
class B{A a;
}

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

示例:

class Phone {
public:Phone(string pName) {cout << "phone" << endl;m_PName = pName;}~Phone() {cout << "phone ~" << endl;}string m_PName;
};class Person {
public:Person(string name, string pName) :m_Name(name), phone(pName){cout << "Person" << endl;}~Person() {cout << "Person ~" << endl;}string m_Name;Phone phone;
};void test01() {Person p("张三", "Apple");cout << p.m_Name << endl;cout << p.phone.m_PName << endl;
}int main() {test01();system("pause");return 0;
}

  总结:

    当其他类对象作为本类成员,构造时候先构造类对象,再构造自身

    析构的顺序与构造的相反

静态成员

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

  静态成员分为:

    静态成员变量

      所有对象共享的一份数据

      在编译阶段分配内存

      类内生命,类外初始化

    静态成员函数

      所有对象共享同一个函数

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

class Person{
public:static int m_A;
};int Person::m_A = 100;void test01(){Person p;cout << p.m_A << endl;Person p2;p.m_A = 200;//输出的值为200,所以说明所有对象共享同一个数据cout << p.m_A << endl;
}void test02(){/*Person p;cout << p.m_A << endl;*/cout << Person::m_A << endl;
}int main(){test01();system("pause");return 0;
}

示例:

class Person{
public:static void func(){cout << "func" << endl;}static int m_A;int m_B;private:static void func2(){cout << "func2" << endl;}
};int Person::m_A = 10;void test01(){Person p1;p1.func();Person::func();//访问不到//Person::func2();
}int main(){test01();system("pause");return 0;
}

C++对象模型和this指针

成员变量和成员函数分开存储

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

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

示例:

class Person{
public:int m_A;static int m_B;void func(){}static void func2(){}
};int Person::m_B = 0;void test01(){Person p;cout << sizeof(p) << endl;
}void test02(){Person p;cout << sizeof(p) << endl;
}int main(){system("pause");return 0;
}

  总结

    空对象占用内存空间为1

    C++编译器会给每个空对象也分配一个空间,时为了区分空对象占内存的情况

    每个空对象也应该有一个独一无二的内存地址

this指针概念

  C++通过提供特殊的对象指针,this指针,解决区分某个对象调用自己,this指向被调用的成员函数所属的对象

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

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

  this指针的用途:

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

    在类的非静态成员函数中返回对象本身,可使用 return *this

示例:

class Person{
public:Person(int age){this -> age = age;}Person& PersonAddAge(Person &p){this -> age +=p.age;return *this;}int age;
};void test01(){Person p1(18);cout << p1.age << endl;
}void test02(){Person p1(10);Person p2(10);p2.PersonAddAge(p1).PersonAddAge(p1);cout << p2.age << endl;
}int main(){test01();test02();system("pause");return 0;
}

空指针访问成员函数

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

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

示例:

class Person {
public:void showClassName() {cout << "Person" << endl;}void showPersonAge() {if (this == NULL) {return;}cout << m_Age << endl;}int m_Age;
};void test01() {Person* p = NULL;p->showClassName();//报错,因为传入的指针为空//p -> showPersonAge();
}int main() {test01();system("pause");return 0;
}

const修饰成员函数

  常函数

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

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

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

  常对象

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

    常对象只能调用常函数

示例:

class Person{
public:void showPerson() const{//this -> m_A = 100;this -> m_B = 100;}int m_A;mutable int m_B;void func(){}
};void test01(){Person p;p.showPerson();
}void test02(){const Person p;//p.m_A = 100;p.m_B = 100;//p.func();
}int main(){test01();test02();system("pause");return 0;
}

  总结:

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

    this指针不可以修改指针的指向的

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

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

    加了muteble的特殊变量,在常对象下也可以修改

    常对象只能调用常函数,不可以调用普通成员函数,因为普通成员函数可以修改属性

友元

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

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

  友元的关键字为 friend

  友元的三种实现

    全局函数做友元

    类做友元

    成员函数做友元

全局函数做友元

class Building {//goodGay可以访问Building中的私有成员friend void goodGay(Building& building);public:string m_SittingRoom;Building() {m_SittingRoom = "客厅";m_BedRoom = "卧室";}private:string m_BedRoom;
};void goodGay(Building& building) {cout << building.m_SittingRoom << endl;cout << building.m_BedRoom << endl;
}void test01() {Building building;goodGay(building);
}int main() {test01();system("pause");return 0;
}

类做友元

class Building;class Building {//GoodGay可以访问本类中的私有成员friend class GoodGay;public:Building();string m_SittingRoom;private:string m_BedRoom;
};class GoodGay {
public:GoodGay();void visit();Building* building;
};void GoodGay::visit() {cout << building->m_SittingRoom << endl;cout << building->m_BedRoom << endl;
}Building::Building() {m_SittingRoom = "客厅";m_BedRoom = "卧室";
}GoodGay::GoodGay() {building = new Building;
}void test01() {GoodGay gg;gg.visit();
}int main() {test01();system("pause");return 0;
}

成员函数做友元

class Building;class GoodGay {
public:GoodGay();//让visit函数可以访问私有成员void visit();void visit2();
private:Building* building;
};class Building {//告诉编译器,GoodGay下的visit成员函数作为本类的友元,可以访问私有的成员friend void GoodGay::visit();public:Building();string m_SittingRoom;private:string m_BedRoom;
};Building::Building() {m_SittingRoom = "客厅";m_BedRoom = "卧室";
}GoodGay::GoodGay() {building = new Building;
}void GoodGay::visit() {cout << building->m_SittingRoom << endl;cout << building->m_BedRoom << endl;
}void GoodGay::visit2(){cout << building -> m_SittingRoom << endl;//cout << building -> m_BedRoom << endl;
}void test01() {GoodGay gg;gg.visit();
}int main() {test01();system("pause");return 0;
}

运算符重载

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

加号运算符重载

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

  注意:

    对于内置的数据类型的表达式的运算符是不可以改变的

    不能滥用运算符重载

#include <iostream>
using namespace std;class Person {
public://成员函数重载+/*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;}*/int m_A;int m_B;
};Person operator+(Person& p1, Person& p2) {Person temp;temp.m_A = p1.m_A + p2.m_B;temp.m_B = p1.m_B + p2.m_B;return temp;
}Person operator+(Person& p1, int a) {Person temp;temp.m_A = p1.m_A + a;temp.m_B = p1.m_B + a;return temp;
}void test01() {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 p4 = p1 + 100;cout << p3.m_A << endl;cout << p3.m_B << endl;cout << p4.m_A << endl;cout << p4.m_B << endl;
}int main() {test01();system("pause");return 0;
}

左移运算符重载

  作用:可以输出自定义数据类型

  重载左移运算符配合友元可以实现输出自定义数据类型

#include <iostream>
using namespace std;class Person {friend ostream& operator<<(ostream& cout, Person p);public:Person(int a, int b) {m_A = a;m_B = b;}private:int m_A;int m_B;
};ostream& operator<< (ostream& cout, Person p) {cout << p.m_A << endl;cout << p.m_B << endl;return cout;
}void test01() {Person p(10,10);cout << p << endl;
}int main() {test01();system("pause");return 0;
}

递增运算符重载

  通过重载递增运算符,实现自己的整型数据

#include <iostream>
using namespace std;class MyInteger {friend ostream& operator<< (ostream& cout, MyInteger myint);public:MyInteger() {m_Num = 0;}//重载前置++运算符//返回引用是为了一直对一个数据进行递增操作MyInteger& operator++() {m_Num++;return *this;}//重载后置++运算符//参数中的占位参数int可以作为区分前置和后置递增MyInteger operator++(int) {MyInteger temp = *this;m_Num++;return temp;}private:int m_Num;
};ostream& operator<< (ostream & cout, MyInteger myint) {cout << myint.m_Num;return cout;
}void test01() {MyInteger myint;cout << ++myint << endl;
}void test02() {MyInteger myint;cout << myint++ << endl;cout << myint << endl;
}int main() {test01();test02();system("pause");return 0;
}

赋值运算符重载

  C++编译器至少给一个类添加4个函数

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

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

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

    4.赋值运算符 operator=,对属性进行值拷贝

  如果类中有属性指向堆区,做复制操作时也会出现深浅拷贝问题

#include <iostream>
using namespace std;class Person {
public:Person(int age) {m_Age = new int(age);}int* m_Age;~Person() {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}Person& operator=(Person& p) {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}m_Age = new int(*p.m_Age);return *this;}
};void test01() {Person p1(16);Person p2(20);Person p3(30);p3 = p2 = p1;cout << *p1.m_Age << endl;cout << *p2.m_Age << endl;cout << *p3.m_Age << endl;
}int main() {test01();system("pause");return 0;
}

关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

示例

#include <iostream>
using namespace std;class Person {
public:Person(string name, int age) {this->age = age;this->name = name;}bool operator==(Person& p) {if (this->age == p.age && this->name == p.name) {return true;}return false;}string name;int age;
};void test01() {Person p1("Tom", 12);Person p2("Tom", 12);cout << (p1 == p2) << endl;
}int main() {test01();system("pause");return 0;
}

函数调用运算符重载

  • 函数调用运算符()也可以重载
  • 由于重载后使用的方式非常像函数的调用,因此成为仿函数
  • 仿函数没有固定方法,非常灵活

示例

#include<iostream>;
using namespace std;class MyPrint {
public:void operator()(string test) {cout << test << endl;}
};void test01() {MyPrint myprint;//由于使用起来类似于函数调用,因此成为仿函数myprint("helloWorld");
}class MyAdd {
public:int operator()(int num1, int num2) {return num1 + num2;}
};void test02() {MyAdd myAdd;int res = myAdd(1, 2);cout << res << endl;
}int main() {test01();test02();system("pause");return 0;
}

继承

继承是面向对象三大特征之一

继承的基本语法

继承的优点:减少重复代码

语法class 子类 : public 父类
子类也被称为派生类
父类也被称为基类

派生类中的成员,包含两大部分

  • 一类是从基类继承过来的,一类是自己增加的成员
  • 从基类继承过来的表现其共性,而新增的成员体现了其个性

示例:

#include<iostream>;
using namespace std;class BasePage {
public:void header() {cout << "这是网页头部" << endl;}void footer() {cout << "这是网页底部" << endl;}void left() {cout << "这是网页左侧边框展示" << endl;}
};class Java : public BasePage {
public:void content() {cout << "Java学科视频" << endl;}
};class Python : public BasePage {
public:void content() {cout << "Python学科视频" << endl;}
};class Cpp : public BasePage {
public:void content() {cout << "C++学科视频" << endl;}
};void test01() {cout << "Java如下" << endl;Java ja;ja.content();ja.footer();ja.header();ja.left();cout << "------------------" << endl;cout << "Python如下" << endl;Python py;py.content();py.footer();py.header();py.left();cout << "------------------" << endl;cout << "C++如下" << endl;Cpp cpp;cpp.content();cpp.footer();cpp.header();cpp.left();
}int main() {test01();system("pause");return 0;
}

继承方式

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

继承方式一共有三种

  • 公共继承
  • 保护继承
  • 私有继承

在这里插入图片描述

示例:

#include <iostream>
using namespace std;class Base1 {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : public Base1 {
public:void func() {//父类中的公共权限成员 到子类中依然是公共权限m_A = 10;//父类中的保护权限成员 到子类中依然是保护权限m_B = 20;//父类中的四有权限成员 子类访问不到//m_C = 30;}
};void test01() {Son1 s1;s1.m_A = 100;//由于是保护权限,所以类外访问不到这个成员//s1.m_B = 100;
}class Son2 : protected Base1 {
public:void func() {m_A = 100;m_B = 100;//私有权限依旧访问不到//m_C = 100;}
};void test02() {Son2 son2;//protected继承方式将父类中的public权限的变量转换成了protected权限,无法类外访问//son2.m_A;//son2.m_B;
}class Son3 : private Base1 {
public:void func() {m_A = 10;m_B = 10;//依旧无法访问到私有权限成员变量//m_C = 10;}
};void test03() {//private继承方式将父类中的成员都变成了private权限,无法在外部访问//m_A = 1;//m_B = 2;
}

继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象?

父类中私有成员也被子类继承了,只是由编译器给隐藏后访问不到

示例:

#include <iostream>;
using namespace std;class Base {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son : public Base {
public:int m_D;
};//利用开发人员命令提示工具查看对象模型
//跳转盘符  F:
//跳转文件路径 cd 具体路径
//查看命名
//cl /d1 reportSingleClassLayout类名 文件名void test01() {cout << "size of Son = " << sizeof(Son) << endl;
}int main() {test01();system("pause");return 0;
}

继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类对象中的构造函数

问题:父类和子类的构造函数谁先调用?

顺序如下:

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

示例:

#include<iostream>;
using namespace std;class Base {
public:Base() {cout << "Base构造函数" << endl;}~Base() {cout << "Base析构函数" << endl;}
};class Son : public Base {
public:Son() {cout << "Son构造函数" << endl;}~Son() {cout << "Son析构函数" << endl;}
};void test01() {Son son;
}int main() {test01();system("pause");return 0;
}

继承同名成员处理方式

问题:当子类成员与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类和父类拥有同名的成员函数,子类会隐藏掉父类中同名成员函数,加作用域可以访问到父类中同名函数

示例:

#include<iostream>
using namespace std;class Base {
public:Base() {m_A = 100;}void func() {cout << "Base调用" << endl;}int m_A;
};class Son : public Base {
public:Son() {m_A = 200;}void func() {cout << "Son调用" << endl;}int m_A;
};void test01() {Son s;cout << s.m_A << endl;//如果通过子类对象访问到父类中的同名成员,需要加作用域cout << s.Base::m_A << endl;
}void test02() {Son s;s.func();s.Base::func();
}int main() {test01();test02();return 0;
}

继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致:

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域

同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式(通过对象和通过类名)

示例:

#include<iostream>;
using namespace std;class Base {
public:static int m_A;static void func() {cout << "Base静态" << endl;}
};int Base::m_A = 100;class Son : public Base {
public:static int m_A;static void func() {cout << "Son静态" << endl;}
};int Son::m_A = 200;void test01() {//通过对象访问Son s;cout << s.m_A << endl;cout << s.Base::m_A << endl;//通过类名访问cout << Son::m_A << endl;cout << Son::Base::m_A << endl;
}void test02() {//通过对象访问Son s;s.func();s.Base::func();//通过类名访问Son::func();Son::Base::func();
}int main() {test01();test02();return 0;
}

多继承语法

C++允许一个类继承多个类

语法:class 子类 : 继承方式 父类1,继承方式 父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

示例:

#include<iostream>;
using namespace std;class Base1 {
public:Base1() {m_A = 1;}int m_A;
};class Base2 {
public:Base2() {m_B = 2;}int m_B;
};class Son : public Base1, public Base2 {
public:Son() {m_C = 3;m_D = 4;}int m_C;int m_D;
};void test01() {Son s;cout << sizeof(s) << endl;cout << s.Base1::m_A << endl;cout << s.Base2::m_B << endl;
}int main() {test01();return 0;
}

菱形继承

菱形继承概念

  • 两个派生类继承同一个基类
  • 又有某个类同时继承两个派生类
  • 这种继承被称为菱形继承,或者钻石继承

案例

在这里插入图片描述

分析案例中出现的问题

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时,会产生二义性
  2. 羊驼继承自动物的数据变成了两份,其实一份即可

示例:

#include<iostream>;
using namespace std;class Animal{
public:int m_Age;
};//因为菱形继承导致数据有两份,资源浪费
//利用虚继承 加上关键字 virtual 变为虚继承
//Animal类称为 虚基类
class Sheep : virtual public Animal {};class Tuo : virtual public Animal{};class SheepTuo : public Sheep,public Tuo{};void test01() {SheepTuo st;st.Sheep::m_Age = 10;st.Tuo::m_Age = 11;//当菱形继承,两个父类拥有相同数据,需要加以作用域区分cout << st.Sheep::m_Age << endl;cout << st.Tuo::m_Age << endl;cout << st.m_Age << endl;}int main() {test01();return 0;
}

多态

多态的基本概念

  多态是C++面向对象三大特性之一

  多态分为两类

    静态多态:函数重载和运算符重载属于静态多态,复用函数名

    动态多态:派生类和虚函数实现运行时多态

  静态多态和动态多态区别:

    静态多态的函数地址早绑定 - 编译阶段确定函数地址

    动态多态的函数地址晚绑定 - 运行阶段确定函数地址

  动态多态满足条件:

    有继承关系

    子类重写父类的虚函数

  动态多态使用:

    父类的指针或引用指向子类对象

  重写:函数返回值类型 函数名 参数列表完全一致成为重写

示例:

#include<iostream>;
using namespace std;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 doSpeak(Animal &animal) {animal.speak();
}void test01() {Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);
}int main() {test01();return 0;
}

多态案例一:计算器类

  案例描述:

    分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算机类

  多态的优点:

    代码组织结构清晰

    可读性强

    利于前期和后期的拓展和维护

示例:

#include<iostream>;
using namespace std;class Caculator {
public:int getResult(string oper) {if (oper == "+") {return num1 + num2;}else if (oper == "-") {return num1 - num2;}else if (oper == "*") {return num1 * num2;}}int num1;int num2;
};void test01() {Caculator c;c.num1 = 1;c.num2 = 2;cout << c.getResult("+") << endl;cout << c.getResult("-") << endl;cout << c.getResult("*") << endl;
}class AbstractCalculator {
public:virtual int getResult() {return 0;}int num1;int num2;
};class AddCalculator : public AbstractCalculator {
public:int getResult() {return num1 + num2;}
};class SubCalculator : public AbstractCalculator {
public:int getResult() {return num1 - num2;}
};class MulCalculator : public AbstractCalculator {
public:int getResult() {return num1 * num2;}
};void test02() {AbstractCalculator* abc = new AddCalculator;abc->num1 = 1;abc->num2 = 2;cout << abc->getResult() << endl;delete abc;abc = new SubCalculator;abc->num1 = 1;abc->num2 = 2;cout << abc->getResult() << endl;delete abc;abc = new MulCalculator;abc->num1 = 1;abc->num2 = 2;cout << abc->getResult() << endl;delete abc;
}int main() {test01();test02();return 0;
}

纯虚函数和抽象类

  在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

  因此可以将虚函数改为纯虚函数

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

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

  抽象类特点

    无法实例化对象

    子类必须重写抽象类中的纯虚函数,否则也属于抽象类

示例:

#include<iostream>
using namespace std;class Base {
public://只要有一个纯虚函数,这个类成为抽象类virtual void func() = 0;
};class Son : public Base{
public:};class Son2 :public Base {
public:virtual void func() {cout << "调用" << endl;}
};void test01() {//无论堆区还是栈区,都无法实例化对象//Base b;//new Base;//抽象类的子类必须重写父类中的纯虚函数,否则也属于抽象类,无法实例化对象//Son s;Base* b = new Son2;b->func();
}int main() {test01();return 0;
}

多态案例二-制作饮品

  案例描述

    制作饮品的大致流程为:煮水、冲泡、导入杯中、加入辅料

  利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

在这里插入图片描述

示例:

#include<iostream>;
using namespace std;class AbstractDrinking {
public:virtual void Boil() = 0;virtual void Brew() = 0;virtual void PourInCup() = 0;virtual void PutSomething() = 0;void makeDrink() {Boil();Brew();PourInCup();PutSomething();}
};class Coffee : public AbstractDrinking {
public:virtual void Boil() {cout << "煮水" << endl;}virtual void Brew() {cout << "冲咖啡" << endl;}virtual void PourInCup() {cout << "倒咖啡" << endl;}virtual void PutSomething() {cout << "加糖" << endl;}
};class Tea : public AbstractDrinking {
public:virtual void Boil() {cout << "煮矿泉水" << endl;}virtual void Brew() {cout << "冲茶叶" << endl;}virtual void PourInCup() {cout << "倒茶叶" << endl;}virtual void PutSomething() {cout << "加枸杞" << endl;}
};void doWork(AbstractDrinking* a) {a->makeDrink();delete a;
}void test01() {doWork(new Coffee);doWork(new Tea);
}int main() {test01();return 0;
}

虚析构和纯虚析构

  多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

  解决方式:

    将父类中的析构函数改为虚析构或者纯虚析构

  虚析构和纯虚析构共性:

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

    都需要有具体的函数实现

  虚析构和纯虚析构区别:

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

  虚析构语法:virtual ~类名(){}

  纯虚析构语法:virtual ~类名(){} = 0;

         类名::~类名(){}

  虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

  如果子类中没有堆区数据,可以不写为虚析构或者纯虚析构

  拥有纯虚析构函数的类也属于抽象类

示例:

#include<iostream>;
#include<string>;
using namespace std;class Animal {
public:Animal() {cout << "Animal构造函数" << endl;}//利用虚析构可以解决 父类指针释放子类对象时不干净的问题//virtual ~Animal() {//	cout << "Animal析构函数" << endl;//}//纯虚析构//需要声明也需要实现//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象virtual ~Animal() = 0;virtual void speak() = 0;
};Animal :: ~Animal() {cout << "Animal纯虚析构" << endl;
}class Cat : public Animal {
public:Cat(string name) {cout << "Cat构造" << endl;m_name = new string(name);}~Cat() {if (m_name != NULL) {cout << "Cat析构" << endl;delete m_name;m_name = NULL;}}virtual void speak() {cout << *m_name << "猫叫" << endl;}string* m_name;
};void test01() {Animal* a = new Cat("Tom");a->speak();//父类指针在析构的时候 不会调用子类中析构函数 导致子类中如果有堆区属性 出现内存泄漏delete a;
}int main() {test01();return 0;
}

多态案例三-电脑组装

  案例描述

    电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)

    将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Inter厂商和Lenovo厂商

    创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

    测试时组装三台不同的电脑进行工作

示例:

#include<iostream>;
using namespace std;class Cpu {
public:virtual void calculate() = 0;
};class VideoCard {
public:virtual void display() = 0;
};class Memory {
public:virtual void storage() = 0;
};class Computer {
public:Computer(Cpu* m_Cpu, VideoCard* m_vc, Memory* m_mem) {cpu = m_Cpu;vc = m_vc;mem = m_mem;}void work() {cpu->calculate();vc->display();mem->storage();}~Computer() {if (cpu != NULL) {delete cpu;cpu = NULL;}if (vc != NULL) {delete vc;vc = NULL;}if (mem != NULL) {delete mem;mem = NULL;}}private:Cpu* cpu;VideoCard* vc;Memory* mem;
};class InterCPU : public Cpu {virtual void calculate() {cout << "Inter Cpu" << endl;}
};class InterVideoCard : public VideoCard {virtual void display() {cout << "Inter VideoCard" << endl;}
};class InterMemory : public Memory {virtual void storage() {cout << "Inter Memory" << endl;}
};class LenovoCPU : public Cpu {virtual void calculate() {cout << "Lenovo Cpu" << endl;}
};class LenovoVideoCard : public VideoCard {virtual void display() {cout << "Lenovo VideoCard" << endl;}
};class LenovoMemory : public Memory {virtual void storage() {cout << "Lenovo Memory" << endl;}
};void test01() {Cpu* inter = new InterCPU;VideoCard* card = new InterVideoCard;Memory* mem = new InterMemory;Computer* c1 = new Computer(inter, card, mem);c1->work();delete c1;Computer* c2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);c2->work();delete c2;
}int main() {test01();return 0;
}

文件操作

  程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放

  通过文件可以将数据持久化

  C++中对文件操作需要包含头文件<fstream>

  文件类型分为两种

    文本文件:文件以文本的ASCII码形式存储在计算机中

    二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

  操作文件的三大类

    ofstream:写操作

    ifstream:读操作

    fstream:读写操作

文本文件

写文件

  写文件步骤如下:

    包含头文件 #include <fstream>

    创建流对象 ofstream ofs;

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

    写数据 ofs << “写入的数据”;

    关闭文件 ofs.close();

  文件打开方式

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::state初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

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

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

示例:

#include <iostream>;
#include <fstream>;
using namespace std;void test01() {ofstream ofs;ofs.open("test.txt", ios::out);ofs << "姓名:张三" << endl;ofs << "性别:男" << endl;ofs << "年龄:18" << endl;ofs.close();
}int main() {test01();return 0;
}

读文件

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

  读文件步骤如下:

    包含头文件 #include <fstream>

    创建流对象 ifstream ifs;

    打开文件并判断文件是否打开成功 ifs.open(“文件路径”,打开方式);

    读数据 四种方式读取

    关闭文件 ifs.close();

示例:

#include<iostream>;
#include<fstream>;
#include<string>;
using namespace std;void test01() {ifstream ifs;ifs.open("test.txt", ios::in);if (!ifs.is_open()) {cout << "打开失败" << endl;return;}//第一种读取方法/*char buffer[1024] = { 0 };while (ifs >> buffer) {cout << buffer << endl;}*///第二种读取方法/*char buffer[1024] = { 0 };while (ifs.getline(buffer, sizeof(buffer))) {cout << buffer << endl;}*///第三种读取方法string buffer;while (getline(ifs, buffer)) {cout << buffer << endl;}//第四种读取方法(不常用)/*char c;while ((c = ifs.get()) != EOF) {cout << c << endl;}*/ifs.close();
}int main() {test01();return 0;
}

二进制文件

  以二进制的方式对文件进行读写操作

  打开方式要指定ios::binary

写文件

  二进制方式写文件主要利用流对象调用成员函数write

  函数原型:ostream& write(const char * buffer,int len);

  参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

示例:

#include<iostream>
#include<fstream>
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ofstream ofs;ofs.open("person.txt", ios::out | ios::binary);Person p = { "张三",18 };ofs.write((const char*)&p, sizeof(Person));ofs.close();
}int main() {test01();return 0;
}

读文件

  二进制方式读文件主要利用流对象调用成员函数read

  函数原型:istream& read(char*buffer,int len);

  参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

示例:

#include<iostream>
#include<fstream>
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ifstream ifs;ifs.open("person.txt", ios::in | ios::binary);if (!ifs.is_open()) {cout << "打开失败" << endl;return;}Person p;ifs.read((char*)&p, sizeof(Person));cout << p.m_Name << endl;cout << p.m_Age << endl;ifs.close();
}int main() {test01();return 0;
) {cout << buffer << endl;}*///第三种读取方法string buffer;while (getline(ifs, buffer)) {cout << buffer << endl;}//第四种读取方法(不常用)/*char c;while ((c = ifs.get()) != EOF) {cout << c << endl;}*/ifs.close();
}int main() {test01();return 0;
}

二进制文件

  以二进制的方式对文件进行读写操作

  打开方式要指定ios::binary

写文件

  二进制方式写文件主要利用流对象调用成员函数write

  函数原型:ostream& write(const char * buffer,int len);

  参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

示例:

#include<iostream>
#include<fstream>
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ofstream ofs;ofs.open("person.txt", ios::out | ios::binary);Person p = { "张三",18 };ofs.write((const char*)&p, sizeof(Person));ofs.close();
}int main() {test01();return 0;
}

读文件

  二进制方式读文件主要利用流对象调用成员函数read

  函数原型:istream& read(char*buffer,int len);

  参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数

示例:

#include<iostream>
#include<fstream>
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ifstream ifs;ifs.open("person.txt", ios::in | ios::binary);if (!ifs.is_open()) {cout << "打开失败" << endl;return;}Person p;ifs.read((char*)&p, sizeof(Person));cout << p.m_Name << endl;cout << p.m_Age << endl;ifs.close();
}int main() {test01();return 0;
}

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

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

相关文章

C#测试调用DotnetSpider爬取网页内容

微信公众号“DotNet”的文章《.NET快速实现网页数据抓取》介绍了调用开源网页爬取模块DotnetSpider爬取cnblog网站文章的基本方式。之前学习过使用HtmlAgilityPack抓取并分析网页内容&#xff0c;DotnetSpider也依赖HtmlAgilityPack模块&#xff0c;不过前者属于轻量、高效的爬…

大数据开发如何管理项目

在面试的时候总是 会问起项目&#xff0c;那在大数据开发的实际工作中&#xff0c;如何做好一个项目呢&#xff1f; 目录 1. 需求分析与项目规划1.1 需求收集与梳理1.2 可行性分析1.3 项目章程与计划 2. 数据准备与处理2.1 数据源接入2.2 数据仓库建设2.3 数据质量管理 3. 系统…

【微服务】Alibaba Cloud Linux环境下Docker以及MySQL安装

部署Docker 1.安装dnf dnf是新一代的rpm软件包管理器 yum -y install dnf2.安装社区版Docker&#xff08;docker-ce&#xff09; 添加docker-ce的dnf源 dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Alibaba Cloud…

MySQL 12种锁:真实业务与流程图解析

文章目录 1. 表级锁&#xff08;Table Lock&#xff09;场景1&#xff1a;全表扫描统计 2. 行级锁&#xff08;Row Lock&#xff09;场景2&#xff1a;修改特定用户信息 3. 全局锁&#xff08;Global Lock&#xff09;场景3&#xff1a;数据备份 4. 意向锁&#xff08;Intent L…

高性能并行计算华为云实验三:蒙特卡罗算法实验

目录 一、实验目的 二、实验说明 三、实验过程 3.1 创建蒙特卡罗算法源码 3.2 Makefile的创建与编译 3.3 主机文件配置与运行监测​​​​​​​ 四、实验结果与分析 4.1 原教程对应的实验结果 4.2 改进后的实验结果 五、实验思考与总结 5.1 实验思考 5.2 实验总结…

firewalld(2)安装、配置文件、规则查询

安装firewalld 我使用的操作系统是debian 12,并没有安装firewalld。 通过apt install firewalld安装firewalld firewalld 本身是一个服务(firewalld.service),可以通过 systemctl 进行启动、停止和重启,而iptables 本身并不是一个服务,而是一个用户空间工具,被用来配置底…

论文浅尝 | 通过基于动态文档知识图谱增强的大语言模型故事理解

笔记整理&#xff1a;许方舟&#xff0c;天津大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/21286 1. 动机 基于大型 Transformer 的语言模型在需要叙事理解的各种任务上取得了令人难以置信的成功&#xff0c;包括…

【Hadoop学习笔记】认识Hadoop

认识Hadoop 从网上找的课程做的笔记&#xff0c;有些图是自己理解画的&#xff0c;可能不正确&#xff0c;可以作为参考&#xff0c;有疑问的地方请直接指出&#xff0c;共同交流。 Hadoop是由Apache基金会开发的一个分布式系统基础架构&#xff0c;主要解决海量数据的存储和海…

[OtterCTF 2018]Recovery

里克必须找回他的文件&#xff01;用于加密文件的随机密码是什么 恢复他的文件 &#xff0c;感染的文件 &#xff1f; vmware-tray.ex 前面导出的3720.dmp 查找一下 搜索主机 strings -e l 3720.dmp | grep “WIN-LO6FAF3DTFE” 主机名 后面跟着一串 代码 aDOBofVYUNVnmp7 是不…

快速应用开发(RAD):加速软件开发的关键方法

目录 前言1. 快速应用开发的概念1.1 什么是快速应用开发&#xff1f;1.2 RAD与传统开发方法的对比 2. 快速应用开发的实施步骤2.1 需求分析与规划2.2 快速原型开发2.3 用户评估与反馈2.4 迭代开发与改进2.5 最终交付与维护 3. 快速应用开发的优点与应用场景3.1 优点3.2 应用场景…

微调Llama2自我认知

一、概述 最近在学习了解大模型微调相关的内容&#xff0c;在学习的过程中也遇到了很多问题&#xff0c;所以将自己的学习过程记录下来&#xff0c;希望对大模型微调感兴趣的小伙伴提供一点帮助&#xff0c;本文主要介绍一下如何通过SFT微调Llama2的自我认知&#xff0c;先看一…

Summaries

摘要是网格项&#xff0c;它利用聚合函数来显示有关所显示数据的摘要信息&#xff1a;总记录计数、最小值等。 GridControl-Grid View Summary Types 汇总 汇总总数&#xff08;GridSummaryItem&#xff09;是根据所有数据网格记录计算的&#xff0c;并显示在视图页脚中。启…

【ACM出版-EI稳检索】第三届金融创新、金融科技与信息技术国际学术会议(FFIT 2024,7月26-28)

第三届金融创新、科技与信息技术国际学术会议&#xff08;FFIT 2024&#xff09;将于2024年07月26-28日于重庆举行。 FFIT2024 将围绕“金融创新”、"金融科技”与“信息技术”等相关最新研究领域&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、…

第三阶段Spark

Spark和PySpark的介绍 PySpark的相关设置 安装PySpark库 pip install pyspark pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark 构建PySpark执行环境入口对象 # 导包 from pyspark import SparkConf, SparkContext# 创建SparkConf类对象 conf SparkConf()…

算法题--华为od机试考试(整数对最小和、素数之积、找城市)

目录 整数对最小和 题目描述 注意 输出描述 示例1 输入 输出 说明 解析 答案 素数之积 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入 输出 说明 解析 找城市 题目描述 输入 输出 示例1 输入 输出 示例2 输入 输出 说明 解析…

Nvidia显卡GeForce Experience录屏操作流程

安装软件 首先我们从英伟达官网下载GeForce Experience程序&#xff0c;安装在电脑中GeForce Experience&#xff08;简称 GFE&#xff09;自动更新驱动并优化游戏设置 | NVIDIA 登录软件 安装完成后登录 开启录屏功能 登录后点击右上角的设置&#xff08;小齿轮图标&#x…

隐藏Python运行产生的缓存文件(__pycache__)

不少同学使用VScode 提交或运行python代码的时候&#xff0c;出现一些缓存文件 类似于(__pycache__) 这种&#xff0c;对于我这种有一丢丢强迫症的人来说&#xff0c;运行一次就得删除一次&#xff0c;那有没有什么办法将其隐藏的&#xff1f; 在vscode编辑器中打开设置&#…

HarmonyOS Next开发学习手册——创建轮播 (Swiper)

Swiper 组件提供滑动轮播显示的能力。Swiper本身是一个容器组件&#xff0c;当设置了多个子组件后&#xff0c;可以对这些子组件进行轮播显示。通常&#xff0c;在一些应用首页显示推荐的内容时&#xff0c;需要用到轮播显示的能力。 针对复杂页面场景&#xff0c;可以使用 Sw…

第2章_开发板使用

文章目录 第2章 开发板使用2.1 硬件连接2.1.1 连接 ST-Link2.1.2 连接 USB 串口2.1.3 连接 SPI 屏 2.2 运行测试程序验证硬件2.2.1 硬件接线&#xff08;RS485、CAN&#xff09;2.2.2 编译工程2.2.3 配置调试器2.2.4 烧录运行 2.3 创建第 1 个工程2.3.1 创建工程2.3.2 选择调试…

动态规划基础练习

我们需要先从数组较大的开始进行处理&#xff0c;每次考察上下左右的&#xff0c;比较当前存储的最大值和转移来的值&#xff0c;哪一个大一点 #define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> using namespace std;int n, m; int a[105][105]; int addx[] { 0,…