C++面向对象程序设计-北京大学-郭炜【课程笔记(六)】
- 1、可变长数组类的实现
- 2、流插入运算符和流提取运算符的重载
- 2.1、对形如cout << 5 ; 单个"<<"进行重载
- 2.2、对形如cout << 5 << “this” ;连续多个"<<"进行重载
- 详解案例
毕业论文:学习速度较慢
开始课程:P19 4-4.可变长数组类的实现
课程链接:程序设计与算法(三)C++面向对象程序设计 北京大学 郭炜
课程PPT:github提供的对应课程PPT
本节课内容相对较难理解,只做两章。
1、可变长数组类的实现
此部分内容不好🦟解释:请听老师讲解,最好自己把代码敲一遍。
P19 4-4.可变长数组类的实现
// 17.cpp
# include<iostream>
using namespace std;
class CArray
{
int size; // 数组元素的个数
int *ptr; // 指向动态分配的数组
public:
CArray(int s=0); // s代表数组元素的个数,构造函数
CArray(CArray & a);
~CArray();
void push_back(int v); // 用于在数组尾部添加一个元素v
CArray & operator = (const CArray & a); // 用于数组对象间的赋值,深拷贝
int length() {return size;} // 返回数组元素个数
// int & CArray::operator[](int i) // 返回值为int 不行!不支持a[i] = 4
int & operator[](int i) // 返回值为int 不行!不支持a[i] = 4,所以返回值定义为引用
{
// 用以支持根据下表访问数组元素,
// 如n=a[i]和a[i]=4;这样的语句
return ptr[i];
}
//非引用的函数返回值不可以作为左值使用
};
// 构造函数
CArray::CArray(int s):size(s)
{
if(s == 0)
ptr = NULL;
else
ptr = new int[s]; // 动态分配一个内存空间
}
// 复制构造函数(防止数组地址冲突)
CArray::CArray(CArray & a)
{
if(!a.ptr)
{
ptr = NULL;
size = 0;
return ;
}
ptr = new int[a.size];
memcpy(ptr, a.ptr, sizeof(int)*a.size); //一共拷贝sizeof(int)*a.size字节的内容
size = a.size;
}
// 析构函数:释放动态内存空间
CArray::~CArray()
{
// []:属于双目运算符
if(ptr) delete [] ptr; //释放动态分配的内存空间
// 释放之前判断一下ptr是不是空指针
}
CArray & CArray::operator=(const CArray & a)
{
// 赋值号的作用是使用“=”左边对象里存放的数组,大小和内容都和右边的对象一样,但是存储空间是不一样的;从而实现一种深拷贝
if(ptr == a.ptr) // 防止a=a这样的复制导致出错
return * this;
if(a.ptr == NULL)
{
// 如果a里面的数组是空的
if(ptr) delete [] ptr;
ptr = NULL;
size = 0;
return *this;
}
if(size<a.size)
{
// 如果原有空间够大,就不用单独分配内存空间了
if(ptr)
{
delete [] ptr;
}
ptr = new int[a.size];
}
memcpy(ptr, a.ptr, sizeof(int)*a.size);
size = a.size;
return * this;
} // CArray & CArray::operator=(const CArray & a)
void CArray::push_back(int v)
{
// 在数组尾部添加一个元素
if(ptr)
{
int * tmpPtr = new int[size+1]; //重新分配空间,采用临时的指针tmpPtr存储
memcpy(tmpPtr, ptr, sizeof(int)*size); //拷贝原数组内容
delete[] ptr; // 删除原本的ptr空间
ptr = tmpPtr; // 将ptr指向刚才零时的指针
}
else // 数组本来就是空的
ptr = new int[1];
ptr[size++] = v; // 加入新的数组元素
}
// 要编写可变长整形数组类,使之能如下使用:
int main()
{
CArray a; // 开始这里的数组是空的
for(int i=0; i < 5; ++i)
{
a.push_back(i); // 要用动态分配的内存来存放数组元素,需要一个指针成员变量
}
CArray a2, a3;
a2 = a; // 要重载“=”
for(int i=0; i<a.length(); ++i)
{
cout << a2[i] << " "; // a2是一个对象名,不是数组名,对象名加中括号需要重载[]
}
a2 = a3; // a2是空的
for(int i=0; i<a2.length(); ++i) // a2.length()返回0
{
cout << a2[i] << " ";
}
a2 = a3;
cout << endl;
a[3] = 100;
CArray a4(a); //CArray的复制构造函数
for(int i=0; i<a4.length(); ++i)
{
cout << a4[i] << "";
}
return 0;
}
Debug:最难听懂的一行代码:“=”运算符重载
a2 = a; // 要重载“=”
a2 = a3;
下面给出了debug图片可供理解。
2、流插入运算符和流提取运算符的重载
课程链接:
5-5流插入运算符和流提取运算符的重载
配合下方代码解释仔细听课,即可听懂。
问题1
:cout << 5 << “this”;为什么能够成立?
问题2
:cout是什么?
- cout 是在 iostream 中定义的,ostream 类
的对象。
问题3
:“<<” 为什么能用在 cout上?
- “<<” 能用在cout 上是因为,在iostream里对 “<<” 进行了重载。
2.1、对形如cout << 5 ; 单个"<<"进行重载
cout << 5 ; 即 cout.operator<<(5);
// 表示对cout这个对象调用operator<<这个成员函数
cout << “this”; 即 cout.operator<<( “this” );
// 有可能按以下方式重载成 ostream类的成员函数:
void ostream::operator<<(int n). // n:可以表示左移参数5
{
…… //输出n的代码
return;
}
2.2、对形如cout << 5 << “this” ;连续多个"<<"进行重载
考虑返回值的问题,如果返回值是void,那么cout还能接着往下运算吗?显然不可以,如果返回值是cout,那么就可以继续调用重载
ostream & ostream::operator<<(int s). // 返回值为ostream的引用
{
…… //输出n的代码
return * this; // this指针指向了成员函数所作用的对象,那么其就是cout。那么返回值就是cout的引用(&cout)
}
ostream & ostream::operator<<( const char * s )
{
…… //输出s的代码
return * this;
}
cout << 5 << “this”; 本质上的函数调用的形式是什么?
cout.operator<<(5).operator<<(“this”);
详解案例
例题:假定c是Complex复数类的对象,现在希望写“cout << c;”,就能以“a+bi”的形式输出c的值,写“cin>>c;”,就能从键盘接受“a+bi”形式的输入,并且使得c.real = a,c.imag = b。
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
class Complex
{
double real, imag;
public:
Complex(double r=0, double i=0):real(r),imag(i) {};
friend ostream & operator<<(ostream & os, const Complex & c);
friend istream & operator>>(istream & is, Complex & c);
};
// cout是ostream的成员函数,所以我们不能去写成ostream的成员函数(已经封装好了),只能写成全局函数;
// 又因为需要访问稀有成员变量,所以要用到友元函数
// 操作数的个数等于这个函数的参数个数。可以为Complex的对象,也可以是Complex的引用(能够节省时间和空间)
// os为cout的引用,即ostream & os = cout;,所以os等于cout;那么返回值也是ostream & os;所以返回值等于cout
ostream & operator<<(ostream & os, const Complex & c)
{
os << c.real << "+" << c.imag << "i" << "i"; // 以"a+bi"的形式输出
return os;
}
istream & operator>>(istream & is, Complex & c)
{
string s;
is >> s; // 将"a+bi"作为字符串读入,"a+bi"中间不能有空格
int pos = s.find("+", 0);
string sTmp = s.substr(0, pos); // 分离出代表实部的字符串
c.real = atof(sTmp.c_str());
// atof库函数能将const char*指针指向的内容转换为float
sTmp = s.substr(pos+1, s.length()-pos-2);
// 分离出代表虚部的字符串
c.imag = atof(sTmp.c_str());
return is;
};
int main()
{
Complex c;
int n;
cin >> c >> n;
cout << c << "," << n;
return 0;
}
//程序运行结果可以如下:
13.2+133i 87
13.2+133i, 87