9、C++高级语法

一、面向对象基础

1 - 面向对象定义

  • C++定义类:C++使用struct、class来定义一个类
  • struct和class区别
    • struct的默认成员权限是public
    • class的默认成员权限是private

2 - 面向对象构造与析构

  • 让自定义的类像内置类型一样
    • 一个int型的变量,可以完成+、-、*、/、比较、输出、++等一系列操作
    • 如果现在有一个自定义的复数类型,那么它的操作应该什么样?
      • 我们希望可以像使用int一样自然的使用它,同时它对我们是一个黑盒,一种抽象
//Complex.h
#pragma once
class Complex
{
public:
	Complex();			// 默认构造函数
	virtual ~Complex();	// 析构函数
};

//Complex.cpp
#include "Complex.h"
#include<iostream>
using namespace std;

Complex::Complex()
{
	cout << "Complex::Complex()" << endl;
}

Complex::~Complex()
{
	cout << "Complex::~Complex()" << endl;
}

//Test.cpp
#include <iostream>
#include "Complex.h"

int main()
{
	Complex c;
	return 0;
}

在这里插入图片描述

3 - 面向对象属性

//Complex.h
#pragma once
class Complex
{
public:
	Complex();			// 默认构造函数
	Complex(double r, double i);
	virtual ~Complex();	// 析构函数

	double GetReal() const;
	void SetReal(double d);
	double GetImage() const;
	void SetImage(double i);
private:
	double _real;          // 复数的实部
	double _image;         // 复数的虚部
};

//Complex.cpp
#include "Complex.h"
#include<iostream>
using namespace std;

Complex::Complex()
{
	cout << "Complex::Complex()" << endl;
}

Complex::Complex(double r,double i)
{
	this->_real = r;
	this->_image = i;
}

Complex::~Complex()
{
	cout << "Complex::~Complex()" << endl;
}

double Complex::GetReal() const { return _real; }
void Complex::SetReal(double d) { _real = d; }
double Complex::GetImage() const { return _image; }
void Complex::SetImage(double i) { _image = i; }

//Test.cpp
#include <iostream>
#include "Complex.h"

using namespace std;

int main()
{
	Complex c(1.0,2.0);
	cout << c.GetReal() << endl;	// 1
	cout << c.GetImage() << endl;	// 2
	c.SetReal(2.0);
	c.SetImage(3.0);
	cout << c.GetReal() << endl;	// 2
	cout << c.GetImage() << endl;	// 3

	return 0;
}

4 - +、=运算符重载

//Complex.h

#pragma once
class Complex
{
public:
	Complex();			// 默认构造函数
	Complex(double r, double i);
	virtual ~Complex();	// 析构函数

	double GetReal() const;
	void SetReal(double d);
	double GetImage() const;
	void SetImage(double i);

	// 运算符重载
	Complex operator+(const Complex& c);
	Complex& operator=(const Complex& c);
	Complex& operator+=(const Complex &c);


private:
	double _real;          // 复数的实部
	double _image;         // 复数的虚部
};

//Complex.cpp
#include "Complex.h"
#include<iostream>
using namespace std;

Complex::Complex()
{
	_real = 0.0;
	_image = 0.0;
}

Complex::Complex(double r, double i)
{
	this->_real = r;
	this->_image = i;
}

Complex::~Complex()
{
	cout << "Complex::~Complex()" << endl;
}

double Complex::GetReal() const { return _real; }
void Complex::SetReal(double d) { _real = d; }
double Complex::GetImage() const { return _image; }
void Complex::SetImage(double i) { _image = i; }

Complex Complex::operator+(const Complex& c)
{
	Complex tmp;
	tmp._real = _real + c._real;
	tmp._image = _image + c._image;
	return tmp;
}


Complex& Complex::operator= (const Complex& c)
{
	if (this != &c)
	{
		_real = c._real;
		_image = c._image;
	}
	return *this;
}

Complex& Complex::operator+= (const Complex& c)
{
	_real += c._real;
	_image += c._image;
	return *this;
}

//Test.cpp
#include <iostream>
#include "Complex.h"

using namespace std;

int main()
{
	Complex a(3.0, 2.0);
	Complex b(2.0, 3.0);
	Complex c;
	c = a + b;

	return 0;
}

5 - 前置后置操作符

//h
	// 前置和后置++
	Complex& operator++();   //前置++
	Complex operator++(int); //后置++
	Complex& operator--();   //前置--
	Complex operator--(int); //后置--

//cpp
Complex& Complex::operator++ () // 前置++
{
	_real++;
	_image++;
	return *this;
}

Complex Complex::operator++ (int) // 后置++
{
	//Complex tmp(*this);
	//_real++;
	//_image++;
	//return tmp;
	return Complex(_real++, _image++);
}

Complex& Complex::operator--()   //前置--
{
	_real--;
	_image--;
	return *this;
}

Complex Complex::operator--(int) //后置--
{
	return Complex(_real--, _image--);
}

//test
int main()
{
	Complex a(3.0, 2.0);
	Complex e;
	e = a++;
	e = ++a;

	return 0;
}

在这里插入图片描述

6 - 比较运算符重载

//h
	bool operator==(const Complex &c) const;
	bool operator!=(const Complex &c) const;

//cpp

bool Complex::operator==(const Complex& c) const
{
	return (_real == c._real) && (_image == c._image);
}

bool Complex::operator!=(const Complex& c) const
{
	return !( (_real == c._real) && (_image == c._image) );
}

二、拷贝构造

1 - 拷贝构造调用时机1

  • 调用拷贝构造的原因:tmp是内部定义的对象,在栈上,出了函数后就会被销毁,C++会自动调用拷贝构造函数
    在这里插入图片描述
  • 自己重写拷贝构造
//h
	Complex(const Complex& x);	// 拷贝构造

//cpp
Complex::Complex(const Complex& c)
{
	_real = c._real;
	_image = c._image;
	cout << "Complex::Complex(const Complex& c)" << endl;
}
  • 优化:避免产生临时对象,不调用拷贝构造
Complex Complex::operator+(const Complex& c)
{
	//Complex tmp;
	//tmp._real = _real + c._real;
	//tmp._image = _image + c._image;
	//return tmp;
	return Complex(_real + c._real, _image + c._image);
}
  • 优化 —— Complex c = a + b:可以非常明显的看到减少了1次构造与析构
    在这里插入图片描述

2 - 拷贝构造调用时机2

在这里插入图片描述

三、输入输出

1 - 类重载输入输出运算符

//h
#pragma once
#include <iostream>
using namespace std;

class Complex
{
public:
	Complex();			// 默认构造函数
	//省略...

	friend ostream& operator<<(ostream& os, const Complex& x);
	friend istream& operator>>(istream& is, Complex& x);

private:
	double _real;          // 复数的实部
	double _image;         // 复数的虚部
};

//cpp
ostream& operator<<(ostream& os, const Complex& x)
{
	os << "real value is  " << x._real << "  image value is " << x._image;
	return os;
}

istream& operator >> (istream& is, Complex& x)
{
	is >> x._real >> x._image;
	return is;
}

//test
#include <iostream>
#include "Complex.h"

using namespace std;

int main()
{
	Complex a(3.0, 2.0);
	Complex e;
	e = a++;
	cout << e << endl;
	e = ++a;
	cout << e << endl;

	cin >> e;
	cout << e << endl;

	return 0;
}

2 - IO流

  • 传统的C中的IO流:函数有printf、scanf、getch、gets等函数,它们存在问题
    • 不可编程,仅仅能识别固有的数据类型
    • 代码的可移植性差,有很多的坑
  • C++中的IO流:istream、ostream,它们的优点
    • 可编程,对于类库的设计者来说很有用
    • 简化编程,能使得I/O的风格一致

在这里插入图片描述

2 - IO缓存区

  • 标准IO提供的三种类型的缓存模式
    • ①.按块缓存:如文件系统
    • ②.按行缓存:\n
    • ③.不缓存

在这里插入图片描述

  • 缓冲区的验证
    • 使用清空缓存区脏数据的前后对比
      在这里插入图片描述

四、文件操作

1 - 文件介绍

  • 输入流的起点和输出流的终点:都可以是磁盘文件
  • C++中的文件:C++把每个文件都看成是一个有序的字节序列,每个文件都以文件结束标志结束
  • 按照文件中数据的组织形式可以把文件分为
    • 文本文件:文件中信息形式为ASCII码文件,每个字符占一个字节
    • 二进制文件:文件中信息的形式与其在内存中的形式相同
  • 文件操作步骤
    • ①.打开文件用于读或写open
    • ②.检查打开是否成功fail
    • ③.读或者写read,write
    • ④.检查是否读完EOF(end of file)
    • ⑤.使用完文件后关闭文件close
  • 文件打开方式
    • ios::in -> 打开文件进行读操作(ifstream默认模式);
    • ios::out -> 打开文件进行写操作(ofstream默认模式);
    • ios::ate -> 打开一个已有输入或输出文件并查找到文件尾;
    • ios::app -> 打开文件以便在文件的尾部添加数据;
    • ios::nocreate -> 如果文件不存在,则打开操作失败;
    • ios::trunc -> 如果文件存在,清除文件原有内容(默认);
    • ios::binary -> 以二进制方式打开;

2 - 文本文件操作

  • 案例:文件流输入输出
#include <iostream>
#include "Complex.h"
#include <fstream>

using namespace std;

int main()
{
	int a;
	int index = 0;
	fstream fout;
	fout.open("testBuffer.txt", ios::app);
	//if (fout.fail()) //等价
	if (!fout)
	{
		cout << "open file failed" << endl;
	}
	while (cin >> a)
	{
		fout << "The numbers are: " << a << endl;
		index++;
		if (index == 5)
		{
			break;
		}
	}
	cin.ignore(numeric_limits<std::streamsize>::max(), '\n');  // 清空缓存区脏数据
	char ch;
	cin >> ch;
	fout << "the last char is: " << ch << endl;
	fout.close();
	return 0;
}

在这里插入图片描述

3 - 二进制文件操作

#include <iostream>
#include <fstream>

using namespace std;

static const int bufferLen = 2048;
bool CopyFile(const string& src, const string& dst)
{
	// 打开源文件和目标文件
	// 源文件以二进制读的方式打开
	// 目标文件以二进制写的方式打开
	ifstream in(src.c_str(), ios::in | ios::binary);
	ofstream out(dst.c_str(), ios::out | ios::binary | ios::trunc);

	// 判断文件打开是否成功,失败返回false
	if (!in || !out)
	{
		return false;
	}

	// 从源文件中读取数据,写到目标文件中
	// 通过读取源文件的EOF来判断读写是否结束
	char temp[bufferLen];
	while (!in.eof())
	{
		in.read(temp, bufferLen);
		streamsize count = in.gcount();//从最后一次读取的长度
		out.write(temp, count);
	}

	// 关闭源文件和目标文件
	in.close();
	out.close();

	return true;
}

int main()
{
	cout << CopyFile("Blue Daube.mp3", "Blue Daube2.mp3") << endl;
	return 0;
}

五、头文件重复包含问题

  • 为了避免同一个文件被include多次,有两种方式:使用宏、使用编译器
  • 方案1:使用宏来防止同一个文件被多次包含
    • 优点:可移植性好
    • 缺点:无法防止宏名重复,难以排错
#ifndef __SOMEFILE_H__
#defind __SOMEFILE_H__
...
#endif
  • 方案2:使用编译器来防止同一文件被多次包含#pragma once
    • 优点:可以防止宏名重复,易排错
    • 缺点:可移植性不好
  • 总结使用:如果只在windows使用使用方案2,其他使用方案1(注意宏不要重名)

六、深拷贝、浅拷贝

  • 浅拷贝:只拷贝指针地址,C++默认拷贝构造函数与赋值运算符重载都是浅拷贝;节省空间,但容易引发多次释放

  • 深拷贝:重新分配堆内存,拷贝指针指向内容;浪费空间,但不会导致多次释放

  • h头文件

#pragma once
#include <iostream>
using namespace std;

class String
{
public:
	String(const char* str = NULL);				// 普通构造函数
	String(const String& other);                // 拷贝构造函数
	String(String&& other);                     // 移动构造函数
	~String(void);                              // 析构函数
	String& operator= (const String& other);    // 赋值函数
	String& operator=(String&& rhs)noexcept;	// 移动赋值运算符

	friend ostream& operator<<(ostream& os, const String& c); // cout输出

private:
	char* m_data; // 用于保存字符串
};
  • cpp实现
#include "String.h"


// _CRT_SECURE_NO_WARNINGS

// String 的普通构造函数
String::String(const char* str)
{
	if (str == NULL)
	{
		m_data = new char[1];
		if (m_data != NULL)
		{
			*m_data = '\0';
		}
		else
		{
			exit(-1);
		}
	}
	else
	{
		int len = strlen(str);
		m_data = new char[len + 1];
		if (m_data != NULL)
		{
			strcpy(m_data, str);
		}
		else
		{
			exit(-1);
		}
	}
}

// 拷贝构造函数
String::String(const String& other)
{
	int len = strlen(other.m_data);
	m_data = new char[len + 1];
	if (m_data != NULL)
	{
		strcpy(m_data, other.m_data);
	}
	else
	{
		exit(-1);
	}
}

// 移动构造函数
String::String(String&& other) //右值引用
{
	if (other.m_data != NULL)
	{
		// 资源让渡
		m_data = other.m_data;
		other.m_data = NULL;
	}
}


// 赋值函数
String& String::operator= (const String& other)
{
	if (this == &other)
	{
		return *this;
	}
	// 释放原有的内容
	delete[] m_data;
	// 重新分配资源并赋值
	int len = strlen(other.m_data);
	m_data = new char[len + 1];
	if (m_data != NULL)
	{
		strcpy(m_data, other.m_data);
	}
	else
	{
		exit(-1);
	}

	return *this;
}

// 移动赋值运算符
String& String::operator=(String&& rhs)noexcept
{
	if (this != &rhs)
	{
		delete[] m_data;
		m_data = rhs.m_data;
		rhs.m_data = NULL;
	}
	return *this;
}

// String 的析构函数
String::~String(void)
{
	if (m_data != NULL)
	{
		delete[] m_data;
	}
}

ostream& operator<<(ostream& os, const String& c)
{
	os << c.m_data;
	return os;
}
  • 测试类
#pragma once

#include "String.h"
using namespace std;


int main()
{

	String s1("Hello");			// 构造函数
	cout << s1 << endl;
	//String s2 = s1;			// 调用拷贝构造函数
	String s2(s1);				// 调用拷贝构造函数
	cout << s2 << endl;
	String s2A(std::move(s1));	// 移动构造函数
	cout << s2A << endl;
	String s3;					// 无参构造函数
	cout << s3 << endl;
	s3 = s2;					// 调用赋值函数
	cout << s3 << endl;
	String s3A;					// 无参构造函数
	s3A = std::move(s2A);		// 移动赋值运算符
	cout << s3A << endl;

	return 0;
}

七、虚函数

#include <iostream>
using namespace std;

// 抽象类
class Shape
{
public:
	virtual double Area() const = 0;// 子类方法实现不一致加上virtual
	virtual void Show() = 0;
	void SetColor(int color) { _color = color; }
	void Display()
	{
		cout << Area() << endl;
	}
private:
	int _color;
};

class Square : public Shape
{
public:
	Square(double len) :_len(len) { }
	void Show() { cout << "Square" << endl; }
	double Area() const
	{
		return _len * _len;
	}
private:
	double _len;
};


class Circle : public Shape
{
public:
	Circle(double radius) :_radius(radius) {}
	void Show() { cout << "Circle" << endl; }
	double Area() const
	{
		return 3.1415926 * _radius * _radius;
	}

private:
	double _radius;
};

class Triangle : public Shape
{
public:
	Triangle(double len, double height) :_len(len), _height(height) {}
	void Show() { cout << "Triangle" << endl; }
	double Area() const
	{
		return 0.5 * _len * _height;
	}

private:
	double _len;
	double _height;
};

int main()
{
	// 面对变化,尽可能少修改原有的逻辑,要扩充逻辑
	const int shapeNum = 3;

	Square s1(2.0);
	s1.SetColor(1);
	Circle   c1(2.0);
	Triangle t1(2.0, 3.0);

	Shape* shapes[shapeNum];
	shapes[0] = &s1;
	shapes[1] = &c1;
	shapes[2] = &t1;

	for (unsigned int index = 0; index < shapeNum; index++)
	{
		shapes[index]->Display();
	}

	cout << endl;
	cout << sizeof(s1) << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无休止符

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值