在对某个类或结构进行运算符重载时,要根据实际情况选择要重载的运算符;
struct Student {
int id;
char name[41];
int age;
int scores[3];
friend ostream &
operator<<(ostream &out, const Student &obj) {
/*return out << "Student{" << obj.id << ",\"" << obj
.name << "\"," << obj.age << ",{" << obj.scores[0]
<< "," << obj.scores[1] << ","
<< obj.scores[2] << "}}";*/
out << "Student{" << obj.id;
out << ",\"" << obj.name << "\",";
out << obj.age << ",{";
out << obj.scores[0] << "," << obj.scores[1] << ","
<< obj.scores[2] << "}}";
return out;
}
friend istream &operator>>(istream &in, Student &obj) {
cout << "input id: ";
in >> obj.id;
in.get();
cout << "input name: ";
in.getline(obj.name, sizeof(obj.name));
cout << "input age: ";
cin >> obj.age;
cout << "input three scores: ";
in >> obj.scores[0] >> obj.scores[1] >> obj.scores[2];
return in;
}
};
1. 针对以上的四个运算符,都是两个对象的运算;v1+v2, v1*v2,v1>v2,v1 2. 重载时,尽量使用友元函数的重载;所谓的友元是站在朋友的立场上,对v1和v2进行操作; 3. 还可使用成员函数进行重载时,没有友元函数重载更加直观和清晰; 4. 在类或结构中,针对特定运算符重载后,在操作对象间的运算时,就像操作内置类型变量的运算一样,比较直观;12+12 v1+v2 v1.add(v2); 5. 在运算符重载这一知识点,在所有的编程语言中,只有C++有这个功能; * 当在Value中重载了大于运算符时,编译器就知道了如何比较 在栈区中分配内存的数组,这种数组是由编译器自动分配内存,自动回收,整个过程不需要程序员干预,只使用即可;这种数组存活均在栈区中; 在堆区存活的数组;需要向堆区中申请内存来存放数组中的所有元素,使用前需要检测内存是否申请成功,使用完后,还需要释放内存,返还堆区; https://pythontutor.com/visualize.html#mode=edit 示例:在下面所示区域输入代码,完成后点击“Visualize Execution”(即将所输入代码可视化执行) 点击过后出现下面右边区域,持续点击Next可观察到整个代码流程 show memory addresses(显示内存地址) byte-level view of data(数据的字节级视图) ⚠️注意:申请时对应删除 指向一个动态数组: 指向一个静态数组: 在以上代码中,涵盖了C++中多种可能出现变量方式; (1)、x; 全局变量;在整个文档中有效; (2)、array|nn; 局部变量(函数中定义的变量称为局部变量),在函数中,从定义时起到整个函数结束; (3)、i;块变量,只在定义它的块中有效; 在C语言和C++中,存活于栈区中的数组,数组名就是一个不带 * 号的常量指针名;它保存了数组元素0的地址; 表示地址时:&array[0] == array ; 访问时:array[0] == *array 注意:在说到数组元素时,不要说:第3个元素;而要说:元素2;对于数组的首元素,要说:元素0,不要说:第一个元素;(即注意⚠️地址下标从0开始) 为什么说数组名是一个常量指针:因为一个数组一旦确定,数组名与它所表示的数组永远绑定,其内存区域永不可改! 上述代码表示:一个变量指针ptr,指向了常量指针array,对于数组的元素操作,除了array外,还可用ptr操作;ptr可以依次指向数组中的不同元素。 强调:这样的结构或类对象,只须活动于内存中,不能写到文件里;无论C语言还是C++,基本操作中不能将指针指向的数组写入到文件中并取出来; 1、 申请内存,用来存放结构对象; 2、 分别申请内存,用来存放分量内容; 3、 每次申请内存,都要进行检测; 4、 使用堆区内存; 5、 使用堆区内存完毕,要释放内存,释放时要注意释放顺序; 6、 顺序原则:先申请的后释放; 7、 具体来说,先释放分量内存,没有顺序; ⚠️堆区中的内存必须释放 最后的安全结果: 在C语言或C++中,对于内存的使用遵循一个大的原则: 尽量少使用栈区内存,尽量多使用堆区内存; 设置一个简单的结构,可以只有一个分量指针,这个指针分量可以是Char*的,也可以是内置类型的;在main函数中,创建一个Test类型的指针,它要向堆区申请内存,存放3个对象;包括赋值和输出; 编码实现以下功能; 代码完成:(⚠️代码还没交由老师检查,不能保证其正确性): 也即是头文件提供的函数;printf,scanf,getchar等;直接应用于程序中的任何角落; 1. 声明;声明于调用函数(多为main函数)之前;也叫函数头或函数原型;声明不需要参数名,有参数类型即可;当然参数也可有名; 2. 定义;定义在调用函数之后;函数功能的具体实现,参数必须有名字; 3. 应用;在调用函数中调用函数; i. 无参、无返回值的函数;void print(); ii. 无参、有返回值的函数;int *get(); iii. 有参、无返回值的函数;void print(const char*); iv. 有参、有返回值的函数;int add(int,int); i. 普通函数; ii. 递归函数;自己调用自己;在实现数据结构时,极为常用;创建二叉树; 从某个角度上说,函数也是一个指针类型,它和数组一样,也是个不带 * 号的常量指针; 在C语言和C++中,只有两个内置的常量指针,就是数组名和函数名; i. 重载函数;C语言中没有这样的函数;C++才出现;思考:何为重载函数? ii. 重写函数;出现在继承链中; iii. 同类函数;在同一个范围(一个源文件,一个类或结构)内的多个函数,具有:返回值类型相同,参数列表相同,函数名不同的函数,称为“同类函数”; int add(int,int); int sub(int,int); int times(int,int); void print(); void hello(); 将某类函数“抽象”为某种数据类型!,就是归纳总结出来函数类型;一旦有了某类函数的数据类型,就应该可以定义变量;既然定义了变量,就可以赋值; 同一范围,多个函数符合:返回值类型相同,参数列表相同,但函数名一定不同; 用到了C语言中提供的关键字:typedef或C++提供的关键字: using; 有一个函数:void apply(int *array,int nn,int (*func)(int));其中参数1和参数2为一个整型数组和数组的长度,参数3为对数组元素的操作。要求自行提供数据,实现这个函数的以下几个功能; 1、 将每个元素值扩大 2 倍; 2、 将每个元素值缩小 3 倍; 3、 将每个元素值平方; 作业思考: 当数组名作函数参数时,它会自动进行了类型退化,变成了一个普通的指针,不再代表整个数组了;如何访问操作数组中的每个元素,如何循环?这时就需要传递一个整数,表示数组长度; 1. 以往我们使用函数时,只须考虑函数的声明、定义及使用三项;在常规应用中,参数参数多为内置类型及数组这样的操作; 2. 学了函数指针后,指针除了指向简单变量、数组后,还可操作函数,还可以做为另一个函数中的参数;一个函数中,可以有一到多个函数做为参数; 1. 指针概念及应用; 2. 二进制文件的操作; 3. 链表; 1. 类的几个默认操作; 2. 运算符的重载;这个功能只有C++有;直观! 3. STL;数据结构的实现; 4. 文件操作;struct Value {
int value;
friend ostream &operator<<(ostream &out, const Value &obj) {
out << "Value{" << obj.value << "}";
return out;
}
friend istream &operator>>(istream &in, Value &obj) {
in >> obj.value;
return in;
}
friend Value operator+(const Value &v1, const Value &v2) {
return Value{v1.value + v2.value};
}
friend Value operator*(const Value &v1, const Value &v2) {
return Value{v1.value * v2.value};
}
friend bool operator>(const Value &v1, const Value &v2) {
return v1.value > v2.value;
}
friend bool operator<(const Value &v1, const Value &v2) {
//return v1.value < v2.value;
return !(v1 > v2);
}
};
void test() {
Value v1{14};
Value v2{24};
Value vsum = v1 + v2;
Value vtimes = v1 * v2;
bool greater = v1 > v2;
cout << vsum << endl;
cout << vtimes << endl;
cout << greater << endl;
bool less = v1 < v2;
cout << less << endl;
bool equals_greater = v1 != v2;
}
* 前者大于后者;但并不知道前者如何小于后者;
* 即知道如何比较 10 > 5,但不知道如何比较 5 < 10;2. 指针指向数组
1. 指针指向静态数组;
2. 指针指向动态数组;
3. 在线测试指针指向数组的功能
4.示例代码:
#include
#include
#include
int *ptr = array;
3. 测试存活于堆区的带指针分量的结构或类
#include
扩展:
#include
下午:
4. 指针指向函数;
1、 通过函数隶属分类(就是函数的主人是谁)
i. 内置函数(预定义函数)
ii. 自定义函数;
void hello(const char *);
void print(int);
void test() {
print(11);
hello("hello,gcc!!");
}
void print(int a) {
cout << "a -> " << (a) << endl;
}
void hello(const char *str) {
cout << "str -> " << (str) << endl;
}
2. 通过函数的参数及返回值类型分类
3. 当多个自定义函数进行交互(调用)时,声明尤为重要
void f0();
void f1();
void f2();
void test() {
}
void f0() {
f2();
}
void f2() {
printf("****** hello,f2() ******\n");
}
void f1() {
f2();
}
4. 根据函数是否循环调用进行分类 (37.44)
5. 函数指针的概念;
1. 以运算符重载为例说明“类比”;
2. 函数指针是指针的第三个作用,就是一个指针变量,用来指向某个函数,就是保存了这个函数的地址;
#include
3. 面向对象编程中函数的分类;
4. 同类函数的“类型”如可设置?
5. 函数类型的抽象:
#include
6.课堂练习
1. 当数组做函数参数时,在C语言或C++中为何必须传递数组的长度?
#include
2. 根据函数指针使用函数数组;
using PtrFunc = int (*)(int);
void test() {
int aa[]{11, 2, 3, 47, 100, 78,};
int nn = sizeof aa / sizeof *aa;
printf("****** 源数组 ******\n");
for (int i = 0; i < nn; ++i) {
cout << setw(5) << aa[i];
}
printf("\n");
printf("\n");
string descriptions[]{
"****** 2倍后 ******",
"****** 缩小3倍后 ******",
"****** 平方后 ******",
};
PtrFunc ff[]{two, divideByThree, square,};
int length = sizeof ff / sizeof *ff;
for (int i = 0; i < length; ++i) {
apply(aa, nn, ff[i]);
cout << descriptions[i] << endl;
for (int i = 0; i < nn; ++i) {
cout << setw(5) << aa[i];
}
printf("\n");
printf("\n");
}
}
3. 示例分析;
4. 学习C语言的几个难点;
5. 学习C++的几个难点;