C++学习笔记

1、引用

作用:给变量起别名

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

int a = 10;
int &b = a;

注意:

  • 引用必须初始化
  • 引用的本质是一个指针常量,引用在初始化后,不可以改变

2、函数调用

  • 函数的调用可以作为左值
  • 函数的默认参数(如果我们自己传入数据,就用自己的数据,如果没有,就用默认值)
  • 如果函数声明有默认参数,函数实现就不能有默认参数

3、函数的重载

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

满足条件:

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

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

当函数重载碰到默认参数,出现二义性,会报错

4、类和对象

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

4.1 封装

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

class student
{
public:
	/*
	类中的属性和行为,统称为  成员
	属性   称为  成员属性  成员方法
	行为   称为  成员函数  成员方法
	*/
	int M_id;  //属性
	void showStudent()  //行为
	{
	cout<<"学号:"<<M_id<<endl;
	}
};

int main(){
	student s1;
	s1.M_id=2;
	s1.showStudent();
	system("pause");
	return 0;
}

访问权限

public: 类内可以访问,类外可以访问

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

private:类内可以访问,类外不可以访问(儿子不可以访问父亲的私有内容)

4.2 struct和class的区别

唯一的区别是 默认的访问权限不同

区别:

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

4.3 成员属性设置为私有

好处:

  • 可以自己控制读写权限
  • 对于写可以检测数据的有效性
//可读可写	
void setName(string name)
	{
		m_Name = name;
	}
	string getName()
	{
		return m_Name;
	}
//只读
int getAge()
{
    m_Age = 0;
    return m_Age;
}
//只写
void setLover(string lover)
{
    m_Lover = love;
}

4.4 立方体类案例

#include<iostream>
using namespace std;
#include<string>

class Cube
{
public:
	void setL(int l)
	{
		L=l;
	}
	int getL()
	{
		return L;
	}
	void setW(int w)
	{
		W=w;
	}
	int getW()
	{
		return W;
	}
	void setH(int h)
	{
		H=h;
	}
	int getH()
	{
		return H;
	}
	int calculateS()
	{
		return 2*L*W+2*H*W+2*L*H;
	}
	int calculateV()
	{
		return L*W*H;
	}
private:
	int L;
	int W;
	int H;

	
};

int main(){
	Cube c1;
	c1.setL(10);
	c1.setH(10);
	c1.setW(10);
	cout<<"面积为"<<c1.calculateS()<<endl;
	cout<<"体积为"<<c1.calculateV()<<endl;

	system("pause");
	return 0;
}

4.5 构造函数和析构函数

构造函数:

语法: 类名(){}

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

析构函数:

语法:~类名(){}

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

4.6 构造函数的分类及调用

分类:

  1. 按照参数分类 无参构造(默认构造)和 有参构造
  2. 按照类型分类 普通构造 拷贝构造
//拷贝构造函数
Person(const Person &P)
{
    //将传入的人身上的所有属性,拷贝到我身上
    age = p.age;
}

调用:

	// 1.括号法
	Person p3(p2);   // 拷贝构造函数
	/*
	调用默认构造函数时,不要加()
	编译器会认为时一个函数的声明,不会认为在创建对象
	*/

	//2.显示法
	Person p3 = Person(p2);
	Person(p2);//匿名对象 当前执行结束后,系统会立即回收掉匿名对象
	/*
	Person(p3)
	不要利用拷贝构造函数 初始化匿名对象,编译器会认为Person(p3)===Person p3;  对象声明
	*/
	//3.隐式转换法
	Person p4 = 10; //相当于写了 Person p4= Person(10); 有参构造
	Person p5 = p4;  //拷贝构造

4.7 初始化列表

/*
Person():m_A(10),m_B(20),m_C(30)
{
	……
}
*/
Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)

4.8 类对象作为类成员

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

class A{}
class B
{
    A a;
}

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

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

4.9 静态成员

  1. 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  2. 静态成员函数
    • 所有对象共享一个函数
    • 静态成员函数只能访问静态成员变量
static void func()
{
    ……
}
void test01()
{
    //1.通过对象访问
    Person P;
    p.func();
   //2.通过类名访问
    Person::func();
}

类外访问不到私有静态成员函数

4.10 this指针

this指针指向被调用的成员函数所属的对象

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

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

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
	Person(int age)
	{
		this->age = age;
		
	}
	Person& PersonAdd(Person p)
	{
		this->age +=p.age;
		//返回对象本身
		return *this;
	}

	int age;
};

4.11 const 修饰成员函数

  1. 常函数:
    • 成员函数后加const后我们称为这个函数为常函数
    • 常函数内不可以修改成员属性
    • 成员属性声明同时加关键字mutable后,在常函数中依然可以修改
  2. 常对象:
    • 声明对象前加const称为该对象为常对象
    • 常对象只能调用常函数
public:
  //this指针的本质是指针常量   指针的指向是不可以修改的
  //在成员函数后面加const,修饰的是this指针,让指针指向的值也不可以修改
  void showPerson() const    //常函数
  {
  	this->m_A=100;
  }
  int m_A;
  
};
void test02()
{
	const Person p;		//在对象前加const,变为常对象
	p.m_A = 100;
	p.showPerson();    //常对象只能调用常函数
}

5、 友元

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

友元的关键字为friend

友元的三种实现:

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
friend void googGay(Building *building);   //全局函数
friend class GoodGay;	//友元类
//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
friend void GoodGay::visit();

6、 运算符重载

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

6.1 加号运算符重载

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

//1. 成员函数重载+号
	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;
	}
//2. 全局函数重载+号
	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=p1+p2

6.2 左移运算符重载

//不会利用成员函数重载<<运算符,因为无法实现cout在左侧   本质p.operator<<(cout)
//只能用全局函数重载左移运算符
ostream & operator<<(ostream &cout,Person &p)
{
    cout<<"m_A="<<p.m_A<<"m_b="<<p.m_B;
    return cout;
}

6.3 递增运算符重载

//重载前置++运算符,返回引用是为了一直对一个数据进行递增操作
MyInteger& operator++()
{
	//先进行++运算符
	m_Num++;
	//再将自身做返回
	return *this;
}

//重载后置++运算符
//void operator++(int)  int代表占位参数,可以区分前置和后置
MyInteger operator++(int)
{
	MyIntger temp = *this;
	m_Num++;
	return temp;
}

6.4 赋值运算符重载

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

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行进行值拷贝
  • 赋值运算符 operator= 对属性进行值拷贝
//重载关系运算符
//重载 == 号
bool operator==(Person &p)
{
	if(this->m_Name ==p.m_Name && this->m_Age == p.m_Age)
	{
		return true;
	}
	return false;
}

7、继承

下级的成员除了拥有上一级的共性,还有自己的特性

优点:减少重复代码

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

子类 (也称为派生类)

父类(也称为基类)

class Java:public BasePage{}

父类中所有非静态成员属性都会被子类继承下去

父类中私有成员属性,是被编译器给隐藏了,因此访问不到,但是确实被继承下去了

7.1 继承中的对象模型

步骤:

  • 利用开发人员命令提示工具查看对象模型
  • 跳转盘符 D:
  • 跳转文件路径 cd 具体路径下
  • 查看命令
  • c1 /d1 reportSingleClassLayout 类名 文件名

继承中构造和析构的顺序如下:

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

7.2 继承中的同名成员处理

cout<<"Son 下m_A="<<s.m_A<<endl;
//如果是通过子类对象 访问父类中同名成员,需要加作用域
cout<<"Base 下m_A="<<s.Base::m_A<<endl;
s.fun();
//调用父类中同名成员函数
s.Base::fun();
s.Base:func(100);
  • 子类对象可以直接访问到子类中同名成员
  • 如果想访问到父类中隐藏的同名成员函数,需要加作用域
  • 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数

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

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

  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域
Son s;
//1.通过对象访问
cout<<"Son 下m_A="<<s.m_A<<endl;
cout<<"Base 下m_A="<<s.Base::m_A<<endl;

//2.通过类名访问
cout<<"Son 下m_A="<<Son::m_A<<endl;
cout<<"Base 下m_A="<<Son::Base::m_A<<endl;

7.4 菱形继承

菱形继承概念:

  • 两个派生类继承同一个基类
  • 又有某个类同时继承着两个派生类
  • 又称钻石继承

菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义

利用虚继承可以解决菱形继承问题

8、多态

8.1 多态的基本概念

多态分为两类

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

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

  • 静态多态的函数地址早绑定 ——编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 ——运行阶段确定函数地址
#include<iostream>
using namespace std;
#include<string>

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;
	}
};
/*动态多态满足条件
	1.有继承关系
	2.子类重写父类的虚函数
*/
//动态多态的使用
//父类的指针或者引用  执向子类对象
void doSpeak(Animal &animal)
{
	animal.speak();
}

void test01()
{
	Cat cat;
	doSpeak(cat);
}
int main(){
	test01();
	system("pause");
	return 0;
}

8.2 纯虚函数和抽象类

在多态中,通常父类中纯虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将 虚函数改为 纯虚函数

语法: virtual 返回值类型 函数名 {参数列表}=0;

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

抽象类的特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

8.3 虚析构和纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

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

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

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

9、文件操作

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

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

C++中对文件操作需要包含头文件====

文件类型分为两种:

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

操作文件的三大类:

  1. ofstream:写操作
  2. ifstream:读操作
  3. fstream:读写操作

9.1文本文件

写文件

步骤:

1.包含头文件

#include

2.创建流对象

ofstream ofs;

3.打开文件

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

4.写数据

ofs<<“写入的数据”;

5.关闭文件

ofs.close();

读文件

步骤:

1.包含头文件

#include

2.创建流对象

ifstream ifs;

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

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

4.读数据

四种方式读取

5.关闭文件

ifs.close();

9.2 二进制文件

写文件:

打开方式要指定为 ios::binary

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

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

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

读文件

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

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

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

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页