您的位置:首页 > 新闻 > 会展 > 网站服务器租用技巧_上海新闻网_韩国日本比分_免费seo网站优化工具

网站服务器租用技巧_上海新闻网_韩国日本比分_免费seo网站优化工具

2025/1/6 18:22:41 来源:https://blog.csdn.net/qq_63958145/article/details/144917731  浏览:    关键词:网站服务器租用技巧_上海新闻网_韩国日本比分_免费seo网站优化工具
网站服务器租用技巧_上海新闻网_韩国日本比分_免费seo网站优化工具

一、友元

前面学习的类中,只能通过该类的公共方法访问私有数据。而如果将某个函数设置为类的友元,那么这个函数就可以直接访问该类的私有数据,破坏了类的封装性,只在某些特定的情况下使用。

友元的分类:普通全局函数作为友元、类的某个成员函数作为友元、整个类作为友元。关键字:friend。

1.全局函数作为类的友元

  • 代码演示
class Object
{friend void useObject(Object &ob);
private:char cup[32];
public:char pen[32];Object(){}Object(char *cup, char *pen){strcpy(this->cup, cup);strcpy(this->pen, pen);}
};// 定义一个全局函数
void useObject(Object &ob)
{cout << "小明正在使用" << ob.pen << endl;cout << "小明正在使用" << ob.cup << endl;
}void test27()
{Object xh_obj("小红的水杯", "小红的钢笔");useObject(xh_obj);
}
  • 运行结果
小明正在使用小红的钢笔
小明正在使用小红的水杯
  • 说明:未设置友元的时候,全局函数直接访问对象的私有数据会报错,但是设置了友元就可以访问了。将全局函数设置友元,只需要在类的首行加上函数的声明,并在前面加上 friend 关键字即可。

2.类的成员函数作为另外一个类的友元

  • 代码演示
// 向前声明Object类
class Object;
// 要将Person类中的成员函数设置为Object的友元,先定义Person类
class Person
{
private:char name[32];
public:Person(){}Person(char *name){strcpy(this->name, name);}void useCup(Object &obj);void usePen(Object &obj);
};class Object
{friend void Person::useCup(Object &obj);
private:char cup[32];
public:char pen[32];Object(){}Object(char *cup, char *pen){strcpy(this->cup, cup);strcpy(this->pen, pen);}
};// 将Person的函数在两个类的下方首先,否则没法识别到Object的数据
void Person::useCup(Object &obj) {cout << name << "正在使用" << obj.cup << endl;
}void Person::usePen(Object &obj) {cout << name << "正在使用" << obj.pen << endl;
}void test27()
{Object xh_obj("小红的水杯", "小红的钢笔");Person xm("小明");xm.usePen(xh_obj);xm.useCup(xh_obj);
}
  • 运行结果
小明正在使用小红的钢笔
小明正在使用小红的水杯
  • 说明:假设将 A 类中的成员函数设置为 B 类的友元,其步骤如下:
  1. 先定义 A 类,并且 A 类的成员函数在类外(A 和 B 类的下方)实现;
  2. 向前声明 B 类,在 A 类前面声明 B 类;
  3. 在 B 类中使用 friend 声明 A 的成员函数为友元。

3.一个类作为另外一个类的友元

  • 代码演示
// 其它代码和上面类的成员函数作为另外一个类的友元一样
// 只是不是将单个函数作为友元,而是整个类,只修改一句代码
// 将class Object中的
friend void Person::useCup(Object &obj); // 改为
friend Person;
// 这样,如果Person类中有多个成员函数需要设置为Object的友元
// 不需要一个个函数去 friend ,只需要把整个类 friend 就行
  • 总结
    1. 友元关系不能被继承,继承后面学。即:类A 是父类,类B 是 类A 的朋友,类C 是 类A 的子类,但 类B 不一定是 类C 的朋友;
    2. 友元关系是单向的。即:类 A 是类 B 的朋 友,但类 B 不一定是类 A 的朋友;
    3. 友元关系不具有传递性。即:类 B 是类 A 的朋 友,类 C 是类 B 的朋友,但类 C 不一定是类 A 的朋友。

二、数组类

数组类,本质上是一个类,可以通过这个类创建一个个数组对象,实现数组的部分功能。

  • 代码演示
class Array
{
private:// 数组的实际元素个数(数组大小)int size;// 数组可容纳的元素个数(数组容量)int capacity;// 数组首元素地址int *array;
public://  构造函数Array();Array(int capacity);Array(const Array &obj);// 析构函数~Array();// 数组的功能函数// 返回数组大小和数组容量int getSize();int getCapacity();// 尾部插入数据void pushBack(int num);// 删除尾部数据void popBack();// 遍历数组void printArray();
};Array::Array() {size = 0;// 将容量默认初始化为5capacity = 5;// 为数组申请堆区空间array = new int[capacity];
}Array::Array(int capacity) {size = 0;this->capacity = capacity;// 为数组申请堆区空间array = new int[capacity];
}Array::Array(const Array &obj) {size = obj.size;capacity = obj.capacity;// 为数组申请堆区空间array = new int[capacity];// 将旧对象的数据通过内存拷贝拷贝给新对象memcpy(array, obj.array, sizeof(int) * capacity);
}Array::~Array() {// 释放前先判断数组是否为空if(array != NULL){// 注意释放的是数组,加 []delete [] array;array = NULL;}
}int Array::getSize() {return size;
}int Array::getCapacity() {return capacity;
}void Array::pushBack(int num) {// 插入前先判断数组是否已满if (size == capacity){// 如果满了就为数组增加一倍的空间int *tempArray = new int[capacity * 2];// 将以前的数组数据拷贝给新开辟的数组空间memcpy(tempArray, array, sizeof(int) * capacity);capacity = capacity * 2;// 释放以前的数组delete [] array;// 指向新开辟的空间array = tempArray;}// 末尾插入数据array[size] = num;size++;
}void Array::popBack() {// 先判断数组是否为空if (NULL == array){cout << "数组为空" << endl;return;}size--;
}void Array::printArray() {int i = 0;for (i = 0; i < size; i++){cout << array[i] << " ";}cout << endl;
}void test28()
{// 创建一个数组对象Array array1;// 给数组添加5个元素array1.pushBack(11);array1.pushBack(22);array1.pushBack(33);array1.pushBack(44);array1.pushBack(55);// 打印数组大小容量cout << "size = " << array1.getSize() << " capacity = " << array1.getCapacity() << endl;// 遍历数组array1.printArray();// 添加一个元素,打印大小和容量array1.pushBack(66);cout << "size = " << array1.getSize() << " capacity = " << array1.getCapacity() << endl;array1.printArray();// 删除一个,打印信息array1.popBack();cout << "size = " << array1.getSize() << " capacity = " << array1.getCapacity() << endl;array1.printArray();
}
  • 运行结果
size = 5 capacity = 5 
11 22 33 44 55        
size = 6 capacity = 10
11 22 33 44 55 66     
size = 5 capacity = 10
11 22 33 44 55 
  • 注意:
    1. 数组类中包含数组的必要数据:数组的的大小,即实际的元素个数,数组的容量,数组首元素的地址;
    2. 数组初始化,除了初始化容量以外,还得申请相应大小的堆区空间;
    3. 插入数据的时候,如果数组的大小和容量相等,代表数组已满,要插入需要先追加空间。但追加空间不是在原空间后面追加,而是新申请内存(为原来的内存加新申请的空间的大小总和),然后再将原来空间的数据,拷贝到新空间。不在后面直接追加的原因是,不确定原空间后面的空间是否存储了其它数据,后面添加可能占用非法内存。

三、运算符重载

运算符重载:就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型的运算需求。

关键字:operator 比如重载 + 运算符,其函数名 operator+

一元运算符:运算符需要的运算对象是一个,如i++ --i等;二元运算符:运算符需要的运算对象是两个,如+ - *等。

1.重载运算符的步骤

  1. 首先必须明确:运算符左边的运算符对象是自定义对象还是其他;
  2. 如果左边是其他,必须使用全局函数完成运算符重载,且全局函数必须是友元;
  3. 如果左边是自定义对象,可以使用成员函数或全局函数完成运算符重载,推荐用成员函数实现。如果使用成员函数,可以少一个参数,如果使用全局函数,不可以少参数,且必须把全局函数设置友元。

先大致了解重载运算符的步骤及注意事项,结合下面的案例具体学习。

2.运算符重载应用

2.1全局函数重载 << 运算符

  • 代码演示
// 定义一个 Person 类
class Person
{friend void operator<<(ostream &out, Person &obj);
private:int num;// string 需要包含 <string> 头文件string name;float score;
public:Person(){num = 000;name = "admin";score = 0.0f;}Person(int num, string name, float score){this->num = num;// string 中重载了 = 运算符,因此这里可以直接赋值this->name = name;this->score = score;}void showPerson(){cout << "num = " << num << " name = " << name << " score = " << score << endl;}
};// 重载 << 运算符
void operator<<(ostream &out, Person &obj)
{out << "num = " << obj.num << " name = " << obj.name << " score = " << obj.score << endl;
}void test29()
{// 创建一个对象并初始化Person xm(101, "小明", 88.5);xm.showPerson();// 之前我们想要查看对象信息,需要通过公共方法来访问// 现在可以通过 cout << 对象 直接访问cout << xm;Person xh(102, "小红", 85.0);cout << xh;
}
  • 运行结果
num = 101 name = 小明 score = 88.5
num = 101 name = 小明 score = 88.5
num = 102 name = 小红 score = 85
  • 说明:

    1. 上面重载了 << 运算符,首先 << 左边是一个非自定义对象,因此需要通过全局函数完成运算符重载;
    2. 因为这里是需要通过重载函数操作对象数据的,因此需要将重载函数设置为类的友元;
    3. 这里重载 << 运算符只是针对当前类创建的对象有效;
    4. operator<<为重载函数名,<< 左边的作为函数的第一个参数,右边的作为函数的第二个参数;
    5. cout << xm是编译器优化后的函数调用方式,其本质是operator<<(cout, xm)
  • 还可以完成链式操作,连续输出多个对象:

// 还是上面案例的代码
// 修改函数
ostream& operator<<(ostream &out, Person &obj)
{out << "num = " << obj.num << " name = " << obj.name << " score = " << obj.score << endl;return out;
}
// 函数修改了,记得修改类的友元
friend ostream&  operator<<(ostream &out, Person &obj);
// 在 test29 函数中添加
cout << xh << xm;
  • 运行结果
num = 102 name = 小红 score = 85  
num = 101 name = 小明 score = 88.5
  • 说明:
    1. 上面的链式操作可以一次输出多个对象的信息;
    2. 原理是将 out 返回,即 cout 的引用,那么上一次函数运算的结果 cout 继续作为 << 的左值,完成连续输出,实现链式操作。

2.2全局函数重载 >> 运算符

这里要实现通过cin >> 对象给对象成员赋值

  • 代码演示
// 还是上面的类
// 重载 >> 运算符
istream &operator>>(istream &in, Person &obj) {in >> obj.num >> obj.name >> obj.score;return in;
}
// 将全局函数设置为类的友元
friend istream &operator>>(istream &in, Person &obj);// 调用函数运行
void test30() {Person jack;cin >> jack;cout << jack;
}
  • 运行结果
101 jack 88
num = 101 name = jack score = 88
  • 和上面重载 >> 运算符原理类似,不做过多解释。

2.3全局函数重载 + 运算符

这里要实现两个对象的加法,即:对象1 + 对象2

  • 代码演示
// 还是使用上面的 Person 类
// 重载 + 运算符
Person operator+(Person obj1, Person obj2)
{// 定义一个临时对象用于存放运算结果Person temp;temp.num = obj1.num + obj2.num;temp.name = obj1.name + obj2.name;temp.score = obj1.score + obj2.score;return temp;
}
// 添加友元
friend Person operator+(Person obj1, Person obj2);
// 将之前的重载 << 函数的第二个参数引用传递改为值传递
ostream &operator<<(ostream &out, Person obj) // 同样友元也记得该// 调用函数执行    
void test31()
{Person jack(101, "jack", 88.5);Person rose(102, "rose", 77.5);cout << jack + rose;
}    
  • 运行结果
num = 203 name = jackrose score = 166
  • 说明:
    1. Person operator+(Person obj1, Person obj2)的返回值不能为引用,因为函数运行结束,临时变量 temp 就释放了,如果返回引用的话,后面 cout 输出会访问非法内存,因此,重载 << 运算符的第二个参数也改为值传递了,这样才能类型匹配;
    2. 如果返回值为非引用,那么它返回的就是具体的值,那么重载 + 运算符的第一个参数也要非引用,因为如果进行链式操作,那么会将上一次 + 运算的结果作为下一次 + 运算的左值,但上一次运算结果是具体值,不能对值取引用,因此第一个参数不能直接引用传递;
    3. 如果第一个参数一定要引用传递的话,那么要加 const 修饰为常引用,才能对值取引用。

2.4成员函数重载 + 运算符

上面的案例, + 的左值为自定义对象,因此可以通过成员函数实现运算符重载。

  • 代码演示
// 还是上面的 Person 类,只是在类中添加如下成员函数
// 成员函数重载 + 运算符Person operator+(Person obj){Person temp;temp.num = num + obj.num;temp.name = name +obj.name;temp.score = score + obj.score;return temp;}
// 调用函数执行
void test32()
{Person jack(101, "jack", 88.5);Person rose(102, "rose", 77.5);Person bob(102, "bob", 66.5);cout << jack + rose + bob;
}
  • 运行结果
num = 305 name = jackrosebob score = 232.5
  • 说明:成员函数实现重载,函数调用本质上是:jack.operator+(rose),只是被编译器简化为了jack + rose,因此只需要传一个参数,因为对象调用函数,函数内部会自动生成保存调用该函数的对象的 this 指针,因此函数内部可以直接使用调用该函数的对象的成员数据,可以少传一个参数。

2.5成员函数重载 == 运算符

直接通过 == 比较两个对象是否相等,即实现 对象1 == 对象2的判断。

  • 代码演示
// 还是上面的 Person 类,只是在类中添加如下成员函数
// 成员函数重载 == 运算符bool operator==(Person &obj){if ((num == obj.num) && (name == obj.name) && (score == obj.score))return true;return false;}
// 调用函数执行
void test33()
{Person jack(101, "jack", 88.5);Person rose(102, "rose", 77.5);Person jack2(101, "jack", 88.5);if (jack == jack2){cout << "相等" << endl;}else{cout << "不相等" << endl;}
}
  • 运行结果
相等

2.6重载 ++ – 运算符

++ 和 – 原理相同,这里就以重载 ++ 运算符为例。

重载 ++ 运算符,有两种情况, ++ 在前和在后,但是其函数名都是 operator++ ,而且该运算符的操作数只有一个,该怎样区分两种不同的情况呢,这里就用到了函数的重载,为区分两个函数,还用到了占位参数。

  • 因此分为两种情况, ++ 前置和 ++ 后置:
    1. 当编译器看到 ++a(前置++)时,它就调用 operator++(a), 先++ 后使用;
    2. 当编译器看到 a++(后置++)时,它就调用 operator++(a, int),先使用后++。
2.6.1重载前置 ++
  • 代码演示
// 还是上面的 Person 类,只是在类中添加如下成员函数
// 成员函数重载前置 ++Person& operator++() {num++;name = name + name;score++;return *this;}
// 调用函数执行
void test34() {Person jack(101, "jack", 88.5);Person bob;bob = ++jack;cout << jack;cout << bob;
}
  • 运行结果
num = 102 name = jackjack score = 89.5
num = 102 name = jackjack score = 89.5
  • 说明:重载前置 ++ 的时候,函数没有占位参数。
2.6.2重载后置 ++
  • 代码演示
// 还是上面的 Person 类,只是在类中添加如下成员函数
// 成员函数重载后置 ++Person operator++(int) {Person old_obj = *this;num++;name = name + name;score++;return old_obj;}
// 调用函数执行
void test35() {Person jack(101, "jack", 88.5);Person bob;bob = jack++;cout << jack;cout << bob;
}
  • 运行结果
num = 102 name = jackjack score = 89.5
num = 101 name = jack score = 88.5
  • 说明:
    1. 重载后置 ++ 的时候,函数有占位参数;
    2. 函数的返回值不能是引用,因为返回的是一个临时局部变量,保存的对象自增前的值,如果返回引用,变量在函数调用结束后就释放了,操作释放的变量会访问非法内存。

2.7重载 () 运算符

我们在前面调用函数的时候,都会在函数名后面加 () 表示调用函数,因此 () 又叫做函数调用运算符。重载函数调用运算符,实现通过对象加()完成函数调用。

  • 代码演示
class Print {
public:Print &operator()(char *str){cout << str;return *this;}
};void test36()
{Print obj1;obj1("hello");cout << endl;Print print;print("hello ")("world ")("hello ")("friend");cout << endl;Print()("hello ")("world ")("hello ")("friend");cout << endl;
}
  • 运行结果
hello                   
hello world hello friend
hello world hello friend
  • 说明:上面定义的类又叫仿函数。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com