IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++知识点 -> 正文阅读

[C++知识库]C++知识点

目录

1. class和struct的区别

2. 构造函数和析构函数

3. 函数运算符重载

3.1 加号运算符重载

3.2 左移运算符重载

3.3 递增运算符重载

3.4 赋值运算符重载

3.5 关系运算符重载

3.6 关系运算符重载

4. 继承

4.1 概念

4.2 示例

4.3 菱形继承

5. 多态

5.1 普通写法和多态写法实现计算器

5.2?虚析构和纯虚析构

5.3 多态案例——电脑组装

6. 文件操作


1. class和struct的区别

三种访问权限

公共权限(public)类内可以访问类外可以访问
保护权限(protected)类内可以访问类外不可以访问
私有权限(private)类内可以访问类外不可以访问

struct:默认权限是public
class:默认权限是私有private

2. 构造函数和析构函数

????????构造函数和析构函数都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构函数。默认情况下,C++编译器至少给一个类添加4个函数:默认构造函数,默认析构函数,默认拷贝构造函数,幅值运算符 operator= 对属性进行值拷贝。

? ? ? ? 默认情况下无有参数构造函数。

构造函数:用来初始化
?? ??? ?1.没有返回值,不用写void
?? ??? ?2.函数名与类名相同
?? ??? ?3.可以有参数,可以发生重载
?? ??? ?4.创建对象的时候,构造函数会自动调用,且只调用一次
?? ??? ?5.如果写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造
?? ??? ?6.如果写了拷贝构造函数,编译器就不再提供其他构造函数

析构函数:进行清理的操作
?? ??? ?1.没有返回值,不写void
?? ??? ?2.函数名和类名相同,在名称前加~
?? ??? ?3.析构函数不可以有参数,不可以发生重载
?? ??? ?4.对象在销毁前会自动调用析构函数,且只调用一次

class Person
{
public:
	
	Person()
	{
		cout << "默认构造函数" << endl;
	}
	Person(int n)
	{
		cout << "有参构造函数" << endl;
		m = n;
	}

    // 另一种写法,在构造函数中直接赋值
	//Person():m(n)
	//{
	//	  cout << "有参构造函数" << endl;
	//}

	// 1.为什么要用引用?
	// 在执行 b(a)时,会调用拷贝函数在函数内创建一个新的对象,即 Person p(a), 拷贝构造函数又会重复调用拷贝构造函数,因此需要使用引用方式
	Person(Person &p)
	{
		m = p.m;
		cout << "拷贝构造函数" << endl;
	}
		
	~Person()
	{
		cout << "析构函数" << endl;
	}
	
	int m;
};

3. 函数运算符重载

3.1 加号运算符重载

? ? ? ? 加号运算符可在全局函数或类中进行重载,但是二者不可重复出现,编译器无法分辨二者。

class Per
{
public:

	//Per operator+(Per &p)
	//{
	//	Per temp;
	//	temp.m = p.m + this->m;
	//	return temp;
	//}
	
	int m;
};

Per operator+(Per& p1, Per& p2)
{
	Per temp;
	temp.m = p1.m + p2.m;
	return temp;
}

void test()
{
	Per a, b;
	a.m = 5;
	b.m = 3;

	Per c = a + b;
	cout << c.m << endl;
}

3.2 左移运算符重载

? ? ? ? 左移运算符只能使用全局函数进行重载

class Per
{
public:
	int m;
};

// cout在左边,因此只能用全局函数重载
ostream& operator<<(ostream &cout, Per& p)
{
	cout << p.m;
	return cout;
}

void test()
{
	Per a, b;
	a.m = 5;
	b.m = 3;

	cout << a << b << endl;
	return;	
}

3.3 递增运算符重载

? ? ? ? 递增运算符有前置++与后置++重载,前置采用operator++()函数,后置++中在输入参数中添加 int 作为占位参数进行区分。由于后置++中 temp 为一个局部变量,因此不返回Per&。

? ? ? ? 在该段代码中,左移运算符的重置与3.2中略有不同,输入参数类型由 Per& 变成了Per类型,若仍使用Per&作为输入,则在cout << ?b++ << endl 处会报错,执行b++时会将temp赋值给一个Per类型的临时变量,该变量不稳定,因此使用值传递,而非引用。

class Per
{
public:

	// 前置++ 重载
	Per& operator++()
	{
		this->m++;
		return *this;
	}  

	// 后置++ 重载
	Per operator++(int)
	{
		Per temp = *this;
		this->m++;
		return temp;
	}

	int m;
};

// cout在左边,因此只能用全局函数重载,这里的Per类不不使用引用,因为后置++重载中传回的数据是一个临时值。
ostream& operator<<(ostream &cout, Per p)
{ 
	cout << p.m;
	return cout;
}

void test()
{
	Per a, b;
	a.m = 5;
	b.m = 3;

	cout << "++(++a) = " << ++(++a) << endl;
	cout <<  b++ << endl;
	return;
}

3.4 赋值运算符重载

? ? ? ? 返回值为Per会产生一个临时对象,在 p3 = p2 = p1 中报错,临时对象比较危险,因此返回Per&。

class Per
{
public:

	Per(int age)
	{
		m_age = new int(age);
	}

	// 若返回的为拷贝,会产生一个临时对象,比较危险,同时会多调用一次构造函数和析构函数
	Per& operator=(Per &p)
	{
		// 编译器是提供浅拷贝
		// m_age = p.m_age;

		// 先判断是否有属性在堆区,如果有先释放干净,再深拷贝
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		m_age = new int(*p.m_age);

		return *this;
	}

	~Per()
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
	}

	int* m_age;
};


void test()
{
	Per p1(18);
	Per p2(20);
	Per p3(30);
	p3 = p2 = p1;

	cout << "p1的年龄:" << *p1.m_age << endl;
	cout << "p2的年龄:" << *p2.m_age << endl;
	cout << "p3的年龄:" << *p3.m_age << endl;
	return;
}

3.5 关系运算符重载

class Person
{
public:

	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}

	bool operator==(Person& p)
	{
		if (this->m_Age == p.m_Age && this->m_Name == p.m_Name)
		{
			return true;
		}
		return false;
	}
	bool operator!=(Person& p)
	{
		if (this->m_Age != p.m_Age || this->m_Name != p.m_Name)
		{
			return true;
		}
		return false;
	}

	string m_Name;
	int m_Age;
	
};

void test()
{
	Person p1("a", 18);
	Person p2("a", 17);

	if (p1 != p2)
	{
		cout << "p1和p2不相等" << endl;
	}
	else
	{
		cout << "p1和p2相等" << endl;
	}
	return;
}

3.6 关系运算符重载

? ? ? ? 关系运算符使用起来非常像函数调用,因此称为仿函数。仿函数非常灵活,没有固定的写法。

class Person
{
public:

	void operator()(string test)
	{
		cout << test << endl;
	}

};

// 仿函数非常灵活,没有固定的写法
class add
{
public:
	int operator()(int a, int b)
	{
		return a + b;
	}
};


void test()
{
	Person a;
	a("jhellop");		// 由于使用起来非常像函数调用,因此称为仿函数
	return;
}

void test2()
{
	add b;
	cout<<b(3, 2)<<endl;

	// 匿名函数对象,当前行执行完立即被释放
	cout << add()(5, 8) << endl;

	return;
}

4. 继承

4.1 概念

????????继承是面向对象的三大特性之一。三大特性:封装、继承、多态。

? ? ? ? 继承的作用:减少重复代码

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

三种继承方式:

????????公共继承、保护继承、私有继承。父类中所有非静态成员属性都会被子类继承下去;私有成员属性是被编译器隐藏了,访问不到,但是被继承下去了

4.2 示例

// 继承的三种方式
// 父类
class A
{
public:
	int a;
protected:
	int b;
private:
	int c;
};

// 公有继承
class B :public A
{
public:
	int a;
protected:
	int b;

// 不可访问
	int c;
};

// 保护继承
class B :protected A
{
protected:
	int a;
	int b;

	// 不可访问
	int c;
};

// 私有继承
class B :private A
{
private:
	int a;
	int b;

	// 不可访问
	int c;
};

4.3 菱形继承

????????子类继承两份相同的数据,资源浪费,毫无意义,利用虚继承可以解决该问题。

? ? ? ? 打开开发命令提示符,跳转到文件目录下,输入 ? cl /d1 reportSingleClassLayoutSheeptuo "源.cpp" 查看类的结构。

? ? ? ? Sheep类和Tuo类中存储的变为了指针,这样可以避免两者重复调用Animal中数据。

// 动物类
class Animal
{
public:
	int age;
};

// 利用虚继承解决菱形继承的问题
// 在继承之前加上关键字virtual变为虚继承
// animal类称为虚基类
// 羊类
class Sheep:virtual public Animal
{
};


// 驼类
class Tuo :virtual public Animal
{            
};

// 羊驼类
class Sheeptuo :public Sheep, public Tuo
{
};

void test()
{
	Sheeptuo st;
	// 两个父类拥有相同数据,需要加作用域区分
	st.Sheep::age = 18; 
	st.Tuo::age = 28;

	cout << st.Sheep::age << endl;
	cout << st.Tuo::age << endl;
}

5. 多态

? ? ? ? 多态分为静态多态和动态多态,静态多态包括函数重载和运算符重载,函数地址在编译阶段确定;动态多态包括派生类和虚函数实现运行时多态,函数地址在运行阶段确定。动态多态需要满足:有继承关系,子类重写父类的虚函数。动态多态使用父类的指针或者引用指向子类对象。

????????当类中有了纯虚函数,这个类也称为抽象类。抽象类无法实例化对象,子类必须重写抽象类中的纯虚函数,否则也属于抽象类。纯虚函数语法如下:

?????????????????????????virtual 返回值类型 函数名 ( 参数列表 ) = 0;

//打开开发命令提示符,跳转到文件目录下,输入 cl /d1 reportSingleClassLayoutAnimal "多态.cpp" 可以查看类的结构
// 不加virtual时,Animal的大小为1字节;加上virtual后,类中会存放 vfptr指向虚函数表,表中存放 &Animal::speak
class Animal
{
public:
	virtual void speak()
	{
		cout << "说话" << endl;
	}
};

class Cat :public Animal
{
public:
};

// 执行说话的函数
// 地址早绑定,在编译阶段确定函数
// 如果想执行猫说话,就不能提前绑定函数地址,需要再运行阶段进行绑定,地址晚绑定
void doSpeak(Animal& animal)	// Animal &animal = cat;
{
	animal.speak();
}

void test()
{
	Cat cat;
	doSpeak(cat);
}

5.1 普通写法和多态写法实现计算器

// 分别用普通写法和多态技术实现计算器
/*
多态优点:
	代码组织清晰
	可读性强
	利于前期和后期的扩展以及维护
*/

// 普通写法
class Cal
{
public:
	int Value(string ope)
	{
		if (ope == "+")
		{
			return m1 + m2;
		}
		else if (ope == "-")
		{
			return m1 - m2;
		}
	}

	// 如果想扩展新的功能,需要修改源码
	// 在真实开发中,提倡开闭原则
	// 开闭原则:对扩展进行开放,对修改进行关闭
	int m1;
	int m2;
};

void test()
{
	Cal c;
	c.m1 = 5;
	c.m2 = 3;

	cout << c.m1 << " + " << c.m2 << " = " << c.Value("+") << endl;
	cout << c.m1 << " - " << c.m2 << " = " << c.Value("-") << endl;
	return;
}

// 利用多态实现计算器
// 实现计算器抽象类
class AbsCal
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int m1;
	int m2;
};

// 加法计算器类
class Add :public AbsCal
{
public:
	virtual int getResult()
	{
		return m1+m2;
	}
};

// 减法计算器类
class Sub :public AbsCal
{
public:
	virtual int getResult()
	{
		return m1 - m2;
	}
};

// 乘法计算器类
class Mul :public AbsCal
{
public:
	virtual int getResult()
	{
		return m1 * m2;
	}
};

void test2()
{
	// 多态使用条件:父类指针或者引用指向子类对象
	AbsCal* abc = new Add;
	abc->m1 = 5;
	abc->m2 = 3;

	cout << abc->m1 << "+" << abc->m2 << "=" << abc->getResult() << endl;
	delete abc;

	abc = new Sub;
	abc->m1 = 5;
	abc->m2 = 3;
	cout << abc->m1 << "-" << abc->m2 << "=" << abc->getResult() << endl;
	delete abc;

	abc = new Mul;
	abc->m1 = 5;
	abc->m2 = 3;
	cout << abc->m1 << "*" << abc->m2 << "=" << abc->getResult() << endl;
	delete abc;
}

5.2?虚析构和纯虚析构

????????多态使用时,如果子类有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。为了解决该问题,需要将父类中的析构函数改为虚析构或者纯虚析构

class Animal
{
public:
	Animal()
	{
		cout << "Animal的构造函数" << endl;
	}

	virtual ~Animal()
	{
		cout << "Animal的析构函数" << endl;
	}
	virtual void speak() = 0;
};

class Cat :public Animal
{
public:
	Cat(string na)
	{
		cout << "Cat构造函数" << endl;
		name = new string(na);
	}

	virtual void speak()
	{
		cout << *name << "猫说话" << endl;
	}

	~Cat()
	{
		if (name != NULL)
		{
			cout << "Cat析构函数" << endl;
			delete name;
			name = nullptr;
		}
	}
	string* name;
};

5.3 多态案例——电脑组装

/*
电脑组装
	电脑主要组成部件为CPU、显卡、内存条
	将每个零件封装出抽象基类,并提供不同的厂商生成不同零件
	创建电脑类让电脑工作的函数,并且调用每个零件的接口
	测试时组装三台不同的电脑进行工作
*/

// 抽象类
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* a, VideoCard* b, Memory* c)
	{
		m_cpu = a;
		m_video = b;
		m_memory = c;
		cout << "电脑组装完成" << endl;
	}
	// 提供工作的函数
	void start()
	{
		m_cpu->calculate();
		m_video->display();
		m_memory->storage();
	}

	~Computer()
	{
		if (m_cpu!=NULL)
		{
			delete m_cpu;
			m_cpu = nullptr;
		}
		if (m_video != NULL)
		{
			delete m_video;
			m_video = nullptr;
		}
		if (m_memory != NULL)
		{
			delete m_memory;
			m_memory = nullptr;
		}
	}

private:
	CPU* m_cpu;
	VideoCard* m_video;
	Memory* m_memory;
};


// Inter零件
class InterCPU :public CPU
{
public:
	virtual void calculate()
	{
		cout << "Inter 的CPU" << endl;
	}
};

class InterVideo :public VideoCard
{
public:
	virtual void display()
	{
		cout << "Inter 的显卡" << endl;
	}
};

class InterMemory :public Memory
{
public:
	virtual void storage()
	{
		cout << "Inter 的内存" << endl;
	}
};

// le零件

class LeCPU :public CPU
{
public:
	virtual void calculate()
	{
		cout << "Le 的CPU" << endl;
	}
};

class LeVideo :public VideoCard
{
public:
	virtual void display()
	{
		cout << "Le 的显卡" << endl;
	}
};

void test1()
{
	// 第一台电脑零件
	CPU* intelCpu = new InterCPU;
	VideoCard* intelVid = new InterVideo;
	Memory* intelmem = new InterMemory;

	// 创建第一台电脑
	Computer* com1 = new Computer(intelCpu, intelVid, intelmem);
	com1->start();
	delete com1;
}

6. 文件操作

????????文件操作需要包含头文件<fstream>,操作文件的三大类:ofstream(写操作)、ifstream(读操作)、fstream(读写操作)。

????????文件类型分两类,文本文件中文件以文本的ASCII码形式存储在计算机;二进制文件中文件以文本二进制形式存储在计算机。

写文件步骤读文件步骤
1.包含头文件1.包含头文件
2.创建流对象
ofstream ofs;
2.创建流对象
ifstream ifs;
3.打开文件
ofs.open("文件路径",打开方式);
3.打开文件
ofs.open("文件路径",打开方式);
4.写数据
????????ofs<<"写入的数据"
4.读数据
四种方式读取
5.关闭文件
ofs.close()
5.关闭文件
ifs.close()?
// 写文件
void test1()
{
	ofstream ofs;

	// 打开方式
	ofs.open("test.txt", ios::out);

	ofs << "姓名:a" << endl;
	ofs << "性别:不知" << endl;

	ofs.close();
	return;
}

// 读文件
void test2()
{
	ifstream ifs;

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

	if (!ifs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	 读数据
	 方法1
	//char buf[1024] = { 0 };
	//while (ifs >> buf)
	//{
	//	cout << buf << endl;
	//}

	 方法2
	//char buf[1024] = { 0 };
	//while (ifs.getline(buf, sizeof(buf)))
	//{
	//	cout << buf << endl;
	//}

	// 方法3
	string buf;
	while (getline(ifs,buf))
	{
		cout << buf << endl;
	}

	 方法4
	//char c;
	//while ((c = ifs.get()) != EOF)   //EOF end of file             get是一个一个读取
	//{
	//	cout << c;
	//}

	ifs.close();
	return;
}

// 二进制方式写文件
void test3()
{
	ofstream ofs;
	ofs.open("test2.txt", ios::out | ios::binary);

	char a[30] = "heloow";

	ofs.write(a, sizeof(a));
	ofs.close();
	return;
}

// 二进制方式读文件
void test4()
{
	char a[1024];

	ifstream ifs;
	ifs.open("test2.txt", ios::in | ios::binary);
	
	if (!ifs.is_open())
	{
		cout << "文件打开失败" << endl;
		return;
	}

	ifs.read(a, sizeof(a));
	cout << sizeof(a) << endl;
	cout << a << endl;
	return;
}

打开文件的方式:

ios::in为读文件而打开文件
ios::out为写文件而打开文件,若文件中已有内容,写入会将原有内容替换
ios::ate初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

文件打开方式可以配合使用,如:ios::binary | ios::out

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:32:27  更:2022-07-03 10:34:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:45:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码