课程地址: https://www.coursera.org/learn/cpp-chengxu-sheji
文章目录
1.1 运算符重载的基本概念
- 运算符重载概念的引出
c++预定义的运算符只能应用与基本的数据类型int,float,bool,
有时候需要对一些自定义的数据类型进行操作对两个复数类进行 相加
- 运算符重载概念
运算符重载的实质是函数重载
返回值类型 operator 运算符(形参表)
{
...
}
- 运算符重载的注意事项
* . :: sizeof 三目
除这些运算符之外,其它运算符都可以重载- 重载之后的运算符不能改变原来的
结合性
和优先级
- 运算符重载的目的应当是对原运算符功能的扩充,以适应更多数据类型。不能毫无依据的进行运算符重载
1.2 赋值运算符的重载
赋值运算符两边的类型可以不匹配
赋值运算符只能重载为成员函数 why
例子:
编写一个长度可变的字符串类string
- 包含一个char *类型的成员变量
class String{
private:
char * str;
public:
String():str(NULL){} //初始化成员列表
const char *c_str() {return str;}
char *operator= (const char *s);
~String();
}
"note: 如果在类里面定义成员函数,则成员函数后面不加分号。"
char * Sring::operator=(const char &s){
if(str == s.str) return *this;
if(str) delete [] str; //如果不为空,先清空
if(s){
str = new char[strlen(s)+1];
strcpy(str,s);
}else
str = NULL;
}
重载赋值运算符的意义—浅复制和深复制
一般类都是浅复制,深复制需要自己定义
- 浅复制/浅拷贝
浅拷贝只是拷贝指针地址,没有拷贝内存空间 - 深拷贝
2.1 运算符重载为友元函数
为什么要将运算符重载为友元函数,举了一个例子:
比如定义了一个complex类,在类里面对赋值运算符进行了重载。
c = c + 5;
这个操作是可以的,但是,c = 5 + c;
这个操作就不行了
2.2 实例-长度可变的整型数组类
这节课是郭老师上
给定一个主函数,让你写支撑这个主函数的类
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 = a3;
for (int i = 0; i < a2.length(); i++)
{
cout << a2[i] << " ";
}
cout << endl;
a[3] = 100;
CArray a4(a);
for (int i = 0; i <a4.length(); i++)
{
cout << a4[i] << " ";
}
}
class CArray {
public:
CArray(int size):size(size) {
if (size)
ptr = new int[size];
else
ptr = NULL;
}
CArray():size(0) {
ptr = NULL;
}
CArray(const CArray &array) { //在这里面实现深拷贝
if (array.ptr != NULL) {
this->size = array.size;
this->ptr = new int[this->size];
memcpy(this->ptr,array.ptr,sizeof(int)*array.size);
}
else {
this->ptr = NULL;
}
}
CArray & operator=(const CArray array) {
if (array.ptr != NULL) {
this->size = array.size;
this->ptr = new int[this->size];
memcpy(this->ptr, array.ptr, sizeof(int)*array.size);
}
else {
this->ptr = NULL;
this->size = 0;
}
return *this;
}
void push_back(int i) {
size++;
if (ptr != NULL) {
int * pTmp = new int[size];
memcpy(pTmp, ptr, sizeof(int)*size);
pTmp[size - 1] = i;
delete[] ptr;
ptr = pTmp;
}
else
{
ptr = new int[1];
ptr[0] = i;
}
}
int length() {
return size;
}
int& operator[](int i) {
return ptr[i];
}
private:
int size;
int *ptr;
};
3.1 流插入运算符和流提取运算符的重载
问题:cout << 5 << “this” ; 为什么成立
- cout 是在 iostream 中定义的一个 ostream类的对象
- << 能用在cout上是因为,在iostream里面对 << 运算符进行了重载
引入具体问题:
- 怎样的重载才能让 cout << 5; 和 cout <<“this”;和 cout << 5 << “this”;都成立
ostream & operator<<(int i)
{
//输出 i
return *this;
}
ostream & operator<<(const char *s)
{
//输出 s
return *this;
}
本质上cout << 5 << "this";
==》 cout.operator<<(5).operator<<("this");
2.补全代码
question
class CStudent{
public: int nAge;
};
int main(){
CStudent s ;
s.nAge = 5;
cout << s <<"hello";
return 0;
}
"error: 没有与这些操作数匹配的运算符"
最直接的想法在 ostream类中新添加一个对运算符<< 的重载。
但是ostream类已经写好了,不能在里面添加了。
answer
"note:重载为普通函数,操作数的个数等于函数参数的个数"
ostream & operator<<( ostream &op,const CStudent &s) {
op << s.nAge ;
return op;
}
note: 如果CStudent中nAge是私有的,那怎么办?
answer
"将<<重载函数定义CStudent类的成友元函数"
friend ostream & operator<<(ostream &op, CStudent &s)
{
s.nAge = 0;
op << s.nAge;
return op;
}
- 友元函数的定义写在类里面和外面有什么区别么?
- 假定c是Complex复数类的对象,现在希望写“cout << c;”,就能以“a+bi”的形式输出c的值,写“cin>>c;”,就能从键盘接受“a+bi”形式的输入,并且使得c.real = a,c.imag = b。
question
int main() {
Complex c;
int n;
cin >> c >> n;
cout << c << "," << n;
return 0;
}
answer
class Complex {
private:
float real, image;
public:
Complex():real(0),image(0) {
}
friend ostream & operator<< (ostream &o, Complex &c);
friend istream & operator>> (istream &i, Complex &c);
};
ostream & operator<< (ostream &o, Complex &c) {
o << c.real << "+" << c.image << "i";
return o;
}
istream & operator>> (istream &i, Complex &c) {
i >> c.real; getchar();
i >> c.image; getchar();
return i;
}
3.2 自增/自减运算符的重载
-
前置/后置运算符
++i, i++
-
前置运算符作为一元运算符重载
++i
-
后置运算符作为二元运算符重载
i++
4 测验
4.1 代码填空
输入
无
输出
3+4i
5+6i
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
class Complex {
private:
double r,i;
public:
void Print() {
cout << r << "+" << i << "i" << endl;
}
// 在此处补充你的代码
};
int main() {
Complex a;
a = "3+4i"; a.Print();
a = "5+6i"; a.Print();
return 0;
}
answer
Complex& operator=(const char *str)// 在此处补充你的代码
{
int length = 0;
while (str[++length]);
char *s = new char[length];
memcpy(s, str, sizeof(char)*length);
char * tmp;
tmp = strtok(s, "+");
this->r = atof(tmp);
tmp = strtok(NULL, "i");
this->i = atof(tmp);
delete[] s;
return *this;
}
4.3 实现Array类
描述
写一个二维数组类 Array2,使得下面程序的输出结果是:
0,1,2,3,
4,5,6,7,
8,9,10,11,
next
0,1,2,3,
4,5,6,7,
8,9,10,11,
#include <iostream>
#include <cstring>
using namespace std;
// 在此处补充你的代码
int main() {
Array2 a(3,4);
int i,j;
for( i = 0;i < 3; ++i )
for( j = 0; j < 4; j ++ )
a[i][j] = i * 4 + j;
for( i = 0;i < 3; ++i ) {
for( j = 0; j < 4; j ++ ) {
cout << a(i,j) << ",";
}
cout << endl;
}
cout << "next" << endl;
Array2 b; b = a;
for( i = 0;i < 3; ++i ) {
for( j = 0; j < 4; j ++ ) {
cout << b[i][j] << ",";
}
cout << endl;
}
return 0;
}
answer
"1. 用指针构建一个二维数组"
"2. 对于a[][]中[]运算符重载的理解"
class Array2 {
int **ptr;
int row, column;
public:
Array2() {
ptr = NULL;
row = 0;
column = 0;
}
Array2(int i, int j) //构建二维数组
{
row = i; column = j;
ptr = new int *[row]; //先构建一个一维指针数组
for (int k = 0; k < row; k++) //遍历指针数组
ptr[k] = new int[column]; //指针数组中的每个元素指向 正常的一维数组
}
Array2 &operator=(const Array2 &a) { //深拷贝
if (a.ptr != NULL) {
this->row = a.row;
this->column = a.column;
this->ptr = new int *[a.row];
for (int k = 0; k < a.row; k++) {
ptr[k] = new int[a.column];
memcpy(this->ptr[k], a.ptr[k], sizeof(int)*a.column);
}
}
else {
this->ptr = NULL;
}
return *this;
}
int* operator[](int i) {
//a[i][j] 不能理解成 a.operator[i].operator[j]
//a[i][j] 要理解乘 (a.operatro[i])[j] 其中 (a.operatro[i])是数组首地址
return ptr[i];
}
int operator()(int i, int j) {
return ptr[i][j];
}
};
4.4 大整数的加减乘除
描述
给出两个正整数以及四则运算操作符(+ - * /),求运算结果。
输入
第一行:正整数a,长度不超过100
第二行:四则运算符o,o是“+”,“-”,“*”,“/”中的某一个
第三行:正整数b,长度不超过100
保证输入不含多余的空格或其它字符
输出
一行:表达式“a o b”的值。
补充说明:
1. 减法结果有可能为负数
2. 除法结果向下取整
3. 输出符合日常书写习惯,不能有多余的0、空格或其它字符