C++学习笔记

C++学习笔记

常量

c++定义常量两种方式:

1.#define宏常量

#define day 7

2.const 修饰的变量

const int day =7;

关键字

见表

标识符

变量

注:变量名、标识符都要见名知意。

sizeof(数据类型/变量)
float 4字节  有效数字范围:7位有效数字
double 8字节 有效数字范围:15~16位有效数字

break的使用:

1.出现在switch语句中;

2.出现在循环语句中;

3.出现在嵌套循环语句中;

continue作用:在循环语句中,跳过本此循环中余下尚未执行的语句,继续执行下一次循环。

goto语句:无条件跳转语句。

如果标记的名称存在,执行到goto语句时,会跳转到标记的位置。

在做值传递的时候,函数的形参发生改变,并不会影响实参。

函数份文件编写

步骤:

1.创建后缀名为.h的文件;

2.创建后缀名为.cpp的源文件;

3.在头文件中写函数的声明;

4.在源文件中写函数的定义。

指针

指针的作用:通过指针简介访问内存。

内存编号是从0开始记录的,一般用十六进制表示;

可以利用指针保存变量地址。

指针的定义:

数据类型 *指针变量名;
int *p;

指针记录变量a的地址:

p = &a;

使用指针:可以使用解引用的方式来使用指针指向的内存。

指针前加*代表解引用,找到指针指向的内存中的数据。

在32位 操作系统下,指针是占4个字节空间大小,不管是什么数据类型;

在64位操作系统下,指针是占8个字节空间大小

空指针:指针变量指向内存编号为0的空间;

用途:初始化指针变量。

注意:空指针指向的内存是不可以访问的。

int *p =NULL;

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

在程序中,尽量避免使用野指针。

const修饰指针有三种情况:

1.const修饰指针 --常量指针

2.const修饰常量 --指针常量

3.const既修饰指针,又修饰常量

const int * p =&a;
常量指针
特点:指针的指向可以修改,但是指针指向的值不可以

int * const p = &a;
指针常量
特点:指针的指向不可以改,指针指向的值可以改

const int * const p = &a;
特点:指针的指向和指针指向的值都不可以改变
int arr[];
int * p = arr;
arr就是数组的首地址
*p  //利用指针访问第一个元素
p++; //让指针向后偏移4个字节,访问下一个元素

指针和函数

作用:利用指针做函数的参数,可以修改实参的值;

地址传递:如果是地址传递,可以修饰实参。

指针、数组、函数

结构体

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

语法:

struct 结构体名 {结构体成员列表};

通过结构体穿件变量的方式有三种:

1.struct 结构体名 变量名;

2.struct 结构体名 变量名 = {成员1值;成员2值.....}

3.定义结构体时顺便创建变量

struct Student
{
    string name;
    int age;
    int score;
};

通过学生类型创建具体学生:

struct Student s1; //s1为结构体变量

给s1属性赋值,通过 . 访问结构体变量的属性:

s1.name = "张三";
s1.age = 18;
s1.score = 100;
```
struct Student s2 = {“李四”,19,80};
```

struct Student
{
    string name;
    int age;
    int score;
}s3;

结构体数组:

作用:将自定义的结构体放入到数组中方便维护。

语法:

struct 结构体名 数组名[元素个数] = { {},{},{},......{} }

结构体数组:

1.定义结构体:

struct Student
{
    string name;
    int age;
    int score;
};

2.创建结构体数组

struct Student stuArray[3] = 
{
    {"张三",18,100},
    {"李四",28,99},
    {"王五",38,66}
};

3.给结构体数组中的元素赋值

stuArray[2].name = "赵六";
stuArray[2].age = 80;
stuArray[2].score = 60;

4.遍历结构体数组

for(int i = 0; i < 3;i++)
{
    cout << stuArray[i].name
            <<stuArray[i].age
            << stuArray[i].score << endl;
}

结构体指针:

作用:利用指针访问结构体中的成员

利用操作符->可以通过结构体指针访问结构体属性。

struct Student
{
    string name;
    int age;
    int score;
};
int main()
{
    struct student s = {"张三",18,100};
    struc * p = &s;
    //通过指针访问结构体变量中的数据
    cout << p->name << p->age << p->score << endl;
}

结构体嵌套

定义老师结构体

struct student
{
    int id;
    string name;
    int age;
    int score;
}
strucu teacher
{
    int id;
    string name;
    int age;
    struct student stu;
}
int main()
{
    teacher t;
    t.id = 10000;
    t.name= "老王";
    t.age = 50;
    t. stu.name = "小王";
    t. stu.age = 20;
    t. stu.score = 60;
}

结构体做函数参数

作用:将结构体作为参数向函数中传递

传递方式有两种:

1.值传递

2.地址传递


struct student
{
    int id;
    string name;
    int age;
    int score;
}
//值传递
void printStudent1(struct student s)
{
    cout << s.name  << s.age << s.score << endl;
}
//地址传递
void printS tudent2(struct student * p)
{
    cout << p->name << p-> age << p-score << endl;
}
int main()
{
    struct student s;
    s.name = "张三";
    s.age = 20;
    s.score = 80;
    printStudent1(s)
    void printStudent2(&s)
}

如果不想修改主函数的数据,用值传递,反正用地址传递。

结构体中const使用场景

作用:用const来防止误操作。

struct student
{
    string name;
    int age;
    int score;
}
//将函数中的形参改为指针,可以减少内存空间 
void printStudent (const student * s)//加入const之后,一旦有修改的操作就会报错,可以放置误操作。
{
    s -> age = 150;//这句程序为修改结构体内容的操作,在加入const关键字后,这段程序将会报错。
    cout << s.name << s.age << s.score << endl;
}
int main()
{
    struct s = {"张三",15,70};//结构体赋值操作
    printStudents(&s)
    cout >> s.age <<endl;
}

结构体案例

案例描述:

学校正在进行毕设项目,每名老师带领5个学生,总共有三名老师,需求如下:

设计学生和老师的结构体 ,其中在老师的结构体中,有老师姓名和一个存放五名学生的数组作为成员,学生的成员有姓名、考试分数,创建数组存放三名老师,通过函数给每个老师及所带的学生赋值,最终打印出老师数据以及老师所带的学生数据。

C++核心编程

主要针对C++ 面向对象编程技术做详解,探讨C++中的核心编程和精髓。

1 内存分区模型

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

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的

  • 全局区:存放全局变量和静态变量以及常量

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

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

内存四区意义:

不同区域存放的数据,赋予不同的生命周期,灵活编程。

new操作符

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

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

语法:

new 数据类型

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

int * p = new int(10);

利用 delete释放

delete p;

利用new创建数组

int * arr = new int [10];

利用delete释放

delete [] arr;

引用

作用:给变量取别名

语法:

数据类型 &别名 = 原名
int a = 10;
// 创建引用
int &b = a;
b = 20;
cout << a << endl;
输出结果为20(即a的值已经被更改了)
1.引用必须初始化
int a = 10;
int &b;// 错误
int &b =a;
2.引用在初始化后,不可以改变
​

引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参。

优点:可以简化指针修改实参。

引用做函数的返回值

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

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

用法:函数调用作为左值

引用做函数的返回值

1.不要返回局部变量的引用

2.函数的调用可以作为左值

引用的本质

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

引用一旦初始化后,就不可以改变

a = 10;
//自动转换为int * const ref = &a;
int& ref = a;
ref = 20; //内部发现ref是引用,自动转换为*ref = 20;

常量引用

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

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

int a = 10;
int & ref = 10;//错误,引用必须引一块合法的内存空间
const int & ref = 10;
//加上const之后,编译器将代码修改为:
int temp = 10;
int & ref = temp;
ref = 20;//错误,加入const之后变为只读,不可以修改

作用演示:

//打印数据函数
void showValue(const int & val)
{
    cout << "val = "<< val << endl;
}
//防止函数修改变量值的误操作

函数提高

函数默认参数

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

语法:

返回值类型 函数名 (参数 = 默认值)
{
​
}
int func(int a = 30,int b = 20 ,int c = 30)
{
    return a + b + c; 
}
//当自己传入了数据,就用自己的数据,如果没有用默认值。
//1、如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都会必须有默认值

//2、如果函数的声明有默认参数,函数实现就不能有默认参数
// 也就是说声明和实现只能有一个有参数。
int func2(int a = 10,int b =10)//函数声明

int func2(int a = 10,int b = 20) 
{
	return a + b;
}
int main()
{
	func2(10,10);//函数的实现
}

函数占位参数

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

语法:

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

}
//占位参数
//占位参数也可以有默认参数
void func(int a,int = 10)//第二个参数就是占位参数
{
	cout << "this is  func" << endl;
 }
 int main()
 {
 	func(10,10);//在调用时还是要传入占位参数类型一致的参数。
 }

函数重载

函数重载概述

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

函数重载满足条件:

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同,或者个数不同,或者顺序不同

注意:函数的返回值不可以作为函数重载的条件,即函数返回值、个数等不同不能作为重载条件

函数重载注意事项

  • 引用作为重载条件

  • 函数重载喷到函数默认参数

类和对象

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

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

封装

封装的意义一:

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

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

封装意义:

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

语法:

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

类中的属性和行为 统称为成员

属性 成员属性 成员变量

行为 成员函数 成员方法

封装意义二:

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

访问权限有三种:

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

  • protected:保护权限 类内可以访问,类外不可以访问 儿子可以访问父亲中的保护内容

  • private:私有权限 类内可以访问,类外不可以访问 儿子不可以访问父亲的私有内容

protected和private的区别:继承的区别

class的默认权限是私有

struct和class区别

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

struct默认权限是公共

成员属性设置为私有

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

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

对象的初始化和清理

构造函数和析构函数

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

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

构造函数语法:

类名()
{
}

1.构造函数,没有返回值也不写void 2.函数名称与类名相同 3.构造函数可以有参数,因此可以发生重载(函数名相同) 4.程序在调用对象时候会自动调用构造,无须手动调用,而且只调用一次

析构函数语法:

~类名()
{
}

1.析构函数,没有返回值也不写void 2.函数名称与类名相同,在名称前加符号~ 3.析构函数不可以有参数,因此不可以发生重载 4.程序在对象销毁前自动调用析构,无需手动调用,而且只会调用一次

eg:

class Person
{
public:
	//1.1 构造函数
	//没有返回值 不用写void
	//函数名 与类名相同
	//构造函数可以有参数,可以发生重载
	//创建对象的时候,构造函数会自动调用,而且只会调用一次
	Person()
	{
		cout << "Person构造函数的调用" << endl;
	}
	//2.析构函数函数 进行清理的操作
	//没有返回值 不写void
	//函数名和类名相同 在名称前加 ~ 
	//析构函数不可以有参数,不可以发生重载
	//对象在销毁前 会自动调用析构函数,而且只会调用一次
	~Person()
	{
		cout << "Person析构函数的调用" << endl;
	}
	//构造喊析构都是必须有的实现,如果我们不提供,编译器会提供一个空实现的构造和析构
};

构造函数的分类及调用

两种分类方式:

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

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

三种调用方式:

  1. 括号法

  2. 显示法

  3. 隐式转换法

拷贝构造函数调用时机

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

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

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

  • 以值的方式返回局部对象

构造函数调用规则

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

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

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

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

构造函数调用规则如下: 如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造。 如果用户定义拷贝构造函数,C++不会再提供其他构造函数

深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作。 深拷贝:在堆区重新申请空间,进行拷贝操作。

初识化列表

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

语法:

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

image-20231010200121749

类对象作为类成员

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

class A{}
class B
{
	A a;
}

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

析构的顺序与构造相反:先构造本类的成员,再构造对象的,析构时,先析构对象,再析构其他类对象。

静态成员

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

静态成员分为:

静态成员变量:

  1. 所有对象共享同一份数据

  2. 在编译阶段分配内存

  3. 类内声明,类外初始化 静态成员变量不属于某个对象上,所有对象都共享同一份数据 因此静态成员变量有两种访问方式 1.通过对象进行访问

    Person p;

    2.通过类名进行访问

    cout << Person::m_A << endl;

    静态成员变量也有访问权限

    private;
    	static int m_B
    	
    在类外无法访问。类外访问不到私有静态成员变量

image-20231010205311436

image-20231010205407776

静态成员函数:

  • 所有 对象共享同一个函数

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

C++对象模型和this指针

成员变量和成员函数分开存储 在C++中,类内的成员变量和成员函数分开存储 只有在非静态成员变量才属于类的对象上

class Person 
{

}
void test01()
{
	Person p;
	cout << sizeof(p) << endl;
}

test01函数输出的结果为1,即空对象占用内存空间是1 C++编译器会给每个空对象也分配一个字节空间,为了区分空对象占内存的位置(每个对象占用独一无二的空间(内存地址),空对象也是)

class Person
{
	int m_A;	//非静态成员变量 属于类的对象上
	static int m_B;	//非静态成员变量  不属于类对象上
	void func(); 	//非静态成员函数  不属于类对象上
}

this指针概念

已知C++中成员变量和成员函数是分开存储的 每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码 那么:这一块代码如何区分哪个对象调用自己呢?

C++通过提供特殊对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象 this指针是隐含每一个非静态成员函数内的一种指针。 this指针不需要定义,直接使用。

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

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

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

//解决名称冲突问题。
class Person
{
public:
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		this->age = age;
	}
	int age;
}
void test01()
{
	Person p1(18);
	//p1在调用,所以this指针指向p1
}

这样写,就可以解决名称冲突问题。

class Person
{
public:
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		this->age = age;
	}
	void  PersonAddAge(Person &p)
	{
		this->age += p.age;
	}
	int age;
}
//返回对象本身用 *this 
void test02()
{
	Person p1(10);
	Person p2(10);
	p2.PersonAddAge(p1);//当这句代码运行完成后,返回的是void类型;
	//若想要实现p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	//则第一个第一个p2.PersonAddAge(p1)调用完成之后,需要返回对象本身
	//则void  PersonAddAge(Person &p)需要修改为:
	Person& PersonAddAge(Person &p)
	{
		this->age += p.age;
		return *this;
	}
	//p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	//这种编程思想为链式编程思想 
}

const修饰成员函数

常函数:

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

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

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

常对象:

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

  • 常对象只能调用常函数

class Person
{
pubulic:
	//this指针的本质 是指针常量 指针的指向不可以修改的
	//this相当于 
	//const Person *const this;
	//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
	void showPerson() const //加了const关键字,是常函数
	{
		
	}
	void func()
	{
	
	}
	int m_A;
	mutable int m_B;//特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
}

void test02()
{
	const Person p;//在对象前加const,变为常对象
	p.m_A = 100;//会报错
	p.m_B = 100;//不会报错,因为m_B加了mutable关键字,在常对象下也可以修改。
	//常对象只能调用常函数
	p.showPerson();//不报错
	p.func();//报错,常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
}

友元

友元的三种实现:

  • 全局函数作友元

  • 类作友元

  • 成员函数作友元

class BUilding 
{
//goodGay全局函数是Building的好朋友,可以访问Building中私有成员
friend void goodGay(Building * building);
public:
	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
public:
	string m_SittingRoom;//客厅
	
private:
	string m_BedRoom;//卧室
};

//全局函数
void goodGay(Building * building)
{
	cout << building->m_SittingRoom << endl;
	cout << building->m_BedRoom << endl;
}
void test01()
{
	Building building;
	goodGay(&building);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值