抽象是一种认识事物本质的方法


从抽象到对面对象编程

C++高级语法_运算符

面向对象——类


  • C++使用struct、class来定义一个类
    struct的默认成员权限是public,
    class的默认成员权限是private
    除此之外,二者基本无差别;
  • 举例:

class Student{
private://成员变量
string name;
double score;
public://成员函数
double GetScore(){
return score;
}
}

面向对象的第一大误区

  • 对象是对现实世界中具体物体的反映,继承是对物体分类的反映?
    NO!

面向对象的抽象法则1-具体类型的抽象

抽象——具体类型

  • 让自定义的类像内置类型一样;
    一个int型变量,可以完成+,-,*,/,比较,输出,++等一系列操作;

如果现在有一个自定义的复数类型,那么它的操作应什么样?我们希望可以像使用int一样自然的使用它,同时它对我们是一个黑盒,一种抽象。

C++高级语法_运算符_02

析构函数添加virtual修饰

对象的属性

private:

一般对应配置一个get(添加const修饰)和set方法

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

运算符重载

operator 运算符

Complex.h

#pragma once

#include <iostream>
using namespace std;

class Complex
{
public:
Complex(); // 默认构造函数
Complex(double r, double i); // 构造函数
virtual ~Complex(); // 析构函数
Complex(const Complex& x); // 拷贝构造
Complex& operator=(const Complex &c); // =号运算符


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

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

bool operator==(const Complex &c) const;
bool operator!=(const Complex &c) const;
bool operator>(const Complex &c) const;
bool operator>=(const Complex &c) const;
bool operator<(const Complex &c) const;
bool operator<=(const Complex &c) const;


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

//protected:

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

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

Complex.cpp

#include "stdafx.h"

Complex::Complex()
{
_real = 0.0;
_image = 0.0;
//cout << "Complex::Complex()" << endl;
}

Complex::Complex(double r, double i)
{
_real = r;
_image = i;
//cout << "Complex::Complex(double r, double i)" << endl;
}

Complex::Complex(const Complex& c)
{
_real = c._real;
_image = c._image;
//cout << "Complex::Complex(const Complex& c)" << endl;
}

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

Complex::~Complex()
{
_real = _image = 0.0;
//cout << "Complex::~Complex()" << endl;
}

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

return Complex(_real + c._real, _image + c._image);
}


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

return *this;
}

Complex Complex::operator-(const Complex &c) const
{
return Complex(_real - c._real, _image - c._image);
}

Complex& Complex::operator-=(const Complex &c)
{
_real -= c._real;
_image -= c._image;

return *this;
}

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

Complex& Complex::operator*=(const Complex &c)
{
Complex tmp(*this); //拷贝构造函数
_real = tmp._real*c._real - _image*c._image;
_image = tmp._real*c._image + tmp._image*c._real;
return *this;
}

Complex Complex::operator/(const Complex &c) const
{
double t = c._real*c._real + c._image*c._image;
return Complex((_real*c._real - _image*(-c._image)) / t, (_real*(-c._image) + _image*c._real) / t);
}

Complex& Complex::operator/=(const Complex &c)
{
Complex tmp(*this); //拷贝构造函数
double t = c._real*c._real + c._image*c._image;
_real = (tmp._real*c._real - tmp._image*(-c._image)) / t;
_image = (tmp._real*(-c._image) + tmp._image*c._real) / t;
return *this;
}

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) );
}

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);
}

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);
}


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--);
}

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;
}

可重载运算符/不可重载运算符

可重载的运算符列表

类型

符号

双目算术运算符

+ (加),-(减),*(乘),/(除),% (取模)

关系运算符

==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)

逻辑运算符

||(逻辑或),&&(逻辑与),!(逻辑非)

单目运算符

+ (正),-(负),*(指针),&(取地址)

自增自减运算符

++(自增),–(自减)

位运算符

(按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)

赋值运算符

=, +=, -=, *=, /= , % = , &=,

空间申请与释放

new, delete, new[ ] , delete[]

其他运算符

()(函数调用),->(成员访问),,(逗号),[](下标)

不可重载的运算符

类型

符号

.

成员访问运算符

., ->

成员指针访问运算符

::

域运算符

sizeof

长度运算符

?:

条件运算符

#

预处理符号

拷贝构造及临时对象的优化

Complex(const Complex& x);
Complex::Complex(const Complex& x)
{
_real = x._real;
_image = x._image;
}
  • 原始临时对象
Complex Complex::operator+(const Complex& x)
{
Complex tmp;
tmp._real = _real + x._real;
tmp._image = _image + x._image;
return tmp;
}
  • 优化临时对象(尽量避免产生临时变量)
Complex Complex::operator+(const Complex& x)
{
return Complex(_real+x._real,_image+x._image);
}

前置与后置操作符

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

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

标准输入输出IO重载

protected修饰符:儿子、孙子及后代可以使用。

friend修饰符:外部可以使用private私有成员。

friend ostream& operator<<(ostream& os,const  Complex &x);
friend istream& operator>>(istream& is,Complex &x);
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;
}

空格和回车确定输入之间的间隔。

IO流基础

I/O流


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

C++高级语法_ios_03

C++高级语法_面向对象_04

IO缓冲区


  • 标准IO提供的三种类型的缓存模式:
    1.按块缓存:如文件系统;
    2.按行缓存:\n;
    3.不缓存;
    C++高级语法_运算符_05
  • 缓冲区案例(按行缓冲)
    C++高级语法_ios_06

#include<iostream>
using namespace std;

int main()
{
int a;
int index = 0;
while(cin>>a)
{
cout<<"The numbers are:"<<a<<endl;
index++;
if(index == 5)
break;
}
//cin.ignore(numeric limits<std::streamsize>::max(),'\n');//清空缓冲区脏数据
char ch;
cin>>ch;
cout<<"The last char is"<<ch<<endl;

return 0;
}

输入:

1 2 3 4 5 6

输出:

The numbers are:1
The numbers are:2
The numbers are:3
The numbers are:4
The numbers are:5
The last char is:6

清除缓冲区:

cin.ignore(numeric limits<std::streamsize>::max(),'\n');//清空缓冲区脏数据

文件操作基础

文件操作


  • 输入流的起点和输出流的终点都可以是磁盘文件;
  • 文件:C++把每个文件都看成是一个有序的字节序列,每个文件都以文件结束标志结束。
  • 按照文件中数据的组织形式可把文件分成为:

    1. 文本文件:文件中信息形式为ASCII码文件,每个字节占一个字节;
    2. 二进制文件:文件中信息的形式与其在内存中的形式相同(大部分文件);


文件操作

文件操作步骤,对于文件操作要做以下事情:


  1. 打开文件用于读和写open;
  2. 检查打开是否成功fail;
  3. 读或者写read,write;
  4. 检查是否读完EOF(end of file);
  5. 使用完文件后关闭close;

文本文件的操作

打开方式

文件的打开方式:

写法

含义

ios::in

打开文件进行读操作(ifstream默认模式)

ios::out

打开文件进行写操作(ostream默认模式)

ios::ate

打开一个已有输入或输出文件并查找到文件尾

ios::app

打开文件以便在文件的尾部添加数据

ios::nocreate

如果文件不存在,则打开操作失败

ios::trunc

如文件存在,清除文件原有内容(默认)

ios::binary

以二进制方式打开

二进制文件的操作

二进制文件:不能用notepad++和记事本正常打开的文件。

#include <string>
#include <fstream>
#include <iostream>
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多次,有两种方式:
    方法一:
#ifndef _SOMEFILE_H_
#define _SOMEFILE_H_
...
#endif

使用宏来防止同一文件被多次包含;

优点:可移植性好;

缺点:无法防止宏名重复,难以排错

方法二:​​#pragma once​​(项目常用)

使用编译器来防止同一文件被多次包含;

优点:可以防止宏名重复,易排错;

缺点:可移植性不好(windows支持,其他平台尽可能让宏不要重名);

深拷贝浅拷贝及move语义的优化

深拷贝与浅拷贝,写时复制



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



深拷贝:重新分配堆内存,拷贝指针指向内容。浪费空间,但不会导致多次释放;
C++高级语法_ios_08



//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);
}else{
exit(-1);
}
}
//赋值函数(对象已存在)
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);
}
}

怎么兼有二者的优点?

方案一:使用引用计数;

方案二:C++新标准的移动语义;

move语义

你不用,我用(注意使用时需要使用&&,两个引用)。

//移动构造函数
String::String(String&& other)
{
if(other.m_data != NULL)
{
//资源让渡
m_data = other.m_data;
other.m_data = NULL://将原来的指针断开
}
}
//移动赋值运算符
String& String::String(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;
}
}

Hack对象模型和虚函数

虚函数可以直接让子类来继承

面向对象三大特性及总结

面向对象三大特性


  • 封装性:数据和代码捆绑在一起,避免外界干扰和不确定性访问,封装可以使得代码模块化;
  • 继承性:让某种类型对象获得另一个类型对象的属性和方法,继承可以扩展已存在的代码;
  • 多态性:同一事物表现出不同事物的能力,即向不同对象会产生不同的行为,多态的目的则是为了接口重用;

面向对象到底是什么


  • 面向对象是软件工程发展到一定阶段为了管理代码和数据提出的一种方法,​​它没有解决以前解决不了的问题,不是万能的;​
  • 面向对象不是对现实世界的映射;但
    它的封装性可以把问题简化,便于抽象;
    它的继承可以减少代码重复,避免重复发明轮子;
    它的多态可以实现灵活的功能扩充,提升开发效率;
  • 面向对象为我们便捷的开发出能适应变化的软件提供了可能,但还不够