精选文章 C++基础知识(四)-友元和运算符重载

C++基础知识(四)-友元和运算符重载

作者:Dachao1013 时间: 2021-02-05 09:43:13
Dachao1013 2021-02-05 09:43:13
【摘要】整理码字不易,养成好习惯,点赞关注,你的支持就是我写下去的动力,谢谢老板。 
 
本文为C++第三篇后续接着这篇文章写,大家可以持续关注,前三篇在主页 
4.5 友元 
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办? 
解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从...

整理码字不易,养成好习惯,点赞关注,你的支持就是我写下去的动力,谢谢老板。

本文为C++第三篇后续接着这篇文章写,大家可以持续关注,前三篇在主页

4.5 友元

类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?

解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解:

比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。

程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。

4.5.1 友元语法

  1. friend关键字只出现在声明处
  2. 其他类、类成员函数、全局函数都可声明为友元
  3. 友元函数不是类的成员,不带this指针
  4. 友元函数可访问对象任意成员属性,包括私有属性

 

class Building;

//友元类

class MyFriend{

public:

//友元成员函数

void LookAtBedRoom(Building& building);

void PlayInBedRoom(Building& building);

};

class Building{

//全局函数做友元函数

friend void CleanBedRoom(Building& building);

#if 0

//成员函数做友元函数

friend void MyFriend::LookAtBedRoom(Building& building);

friend void MyFriend::PlayInBedRoom(Building& building);

#else

//友元类

friend class MyFriend;

#endif

public:

Building();

public:

string mSittingRoom;

private:

string mBedroom;

};

 

void MyFriend::LookAtBedRoom(Building& building){

cout << "我的朋友参观" << building.mBedroom << endl;

}

void MyFriend::PlayInBedRoom(Building& building){

cout << "我的朋友玩耍在" << building.mBedroom << endl;

}

 

//友元全局函数

void CleanBedRoom(Building& building){

cout << "友元全局函数访问" << building.mBedroom << endl;

}

 

Building::Building(){

this->mSittingRoom = "客厅";

this->mBedroom = "卧室";

}

 

int main(){

 

Building building;

MyFriend myfriend;

 

CleanBedRoom(building);

myfriend.LookAtBedRoom(building);

myfriend.PlayInBedRoom(building);

 

system("pause");

return EXIT_SUCCESS;

}

 

 

[友元类注意]

  1. 友元关系不能被继承。
  2. 友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。
  3. 友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。

 

思考: c++是纯面向对象的吗?

如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。

--- Thinking in C++

 

 

4.5.2 课堂练习

请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量操作的方法,频道操作的方法。由于电视机只能逐一调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能,再增加根据输入调台功能。

 

提示:遥控器可作为电视机类的友元类。

class Remote;

 

class Television{

friend class Remote;

public:

enum{ On,Off }; //电视状态

enum{ minVol,maxVol = 100 }; //音量从0到100

enum{ minChannel = 1,maxChannel = 255 }; //频道从1到255

Television(){

mState = Off;

mVolume = minVol;

mChannel = minChannel;

}

 

//打开电视机

void OnOrOff(){

this->mState = (this->mState == On ? Off : On);

}

//调高音量

void VolumeUp(){

if (this->mVolume >= maxVol){

return;

}

this->mVolume++;

}

//调低音量

void VolumeDown(){

if (this->mVolume <= minVol){

return;

}

this->mVolume--;

}

//更换电视频道

void ChannelUp(){

if (this->mChannel >= maxChannel){

return;

}

this->mChannel++;

}

void ChannelDown(){

if (this->mChannel <= minChannel){

return;

}

this->mChannel--;

}

//展示当前电视状态信息

void ShowTeleState(){

cout << "开机状态:" << (mState == On ? "已开机" : "已关机") << endl;

if (mState == On){

cout << "当前音量:" << mVolume << endl;

cout << "当前频道:" << mChannel << endl;

}

cout << "-------------" << endl;

}

private:

int mState; //电视状态,开机,还是关机

int mVolume; //电视机音量

int mChannel; //电视频道

};

 

//电视机调台只能一个一个的调,遥控可以指定频道

//电视遥控器

class Remote{

public:

Remote(Television* television){

pTelevision = television;

}

public:

void OnOrOff(){

pTelevision->OnOrOff();

}

//调高音量

void VolumeUp(){

pTelevision->VolumeUp();

}

//调低音量

void VolumeDown(){

pTelevision->VolumeDown();

}

//更换电视频道

void ChannelUp(){

pTelevision->ChannelUp();

}

void ChannelDown(){

pTelevision->ChannelDown();

}

//设置频道 遥控新增功能

void SetChannel(int channel){

if (channel < Television::minChannel || channel > Television::maxChannel){

return;

}

pTelevision->mChannel = channel;

}

 

//显示电视当前信息

void ShowTeleState(){

pTelevision->ShowTeleState();

}

private:

Television* pTelevision;

};

 

 

//直接操作电视

void test01(){

 

Television television;

television.ShowTeleState();

television.OnOrOff(); //开机

television.VolumeUp(); //增加音量+1

television.VolumeUp(); //增加音量+1

television.VolumeUp(); //增加音量+1

television.VolumeUp(); //增加音量+1

television.ChannelUp(); //频道+1

television.ChannelUp(); //频道+1

television.ShowTeleState();

}

 

//通过遥控操作电视

void test02(){

//创建电视

Television television;

//创建遥控

Remote remote(&television);

remote.OnOrOff();

remote.ChannelUp();//频道+1

remote.ChannelUp();//频道+1

remote.ChannelUp();//频道+1

remote.VolumeUp();//音量+1

remote.VolumeUp();//音量+1

remote.VolumeUp();//音量+1

remote.VolumeUp();//音量+1

remote.ShowTeleState();

}

 

4.5 强化训练(数组类封装)

MyArray.h

#ifndef MYARRAY_H

#define MYARRAY_H

 

class MyArray{

public:

//无参构造函数,用户没有指定容量,则初始化为100

MyArray();

//有参构造函数,用户指定容量初始化

explicit MyArray(int capacity);

//用户操作接口

//根据位置添加元素

void SetData(int pos, int val);

//获得指定位置数据

int GetData(int pos);

//尾插法

void PushBack(int val);

//获得长度

int GetLength();

//析构函数,释放数组空间

~MyArray();

private:

int mCapacity; //数组一共可容纳多少个元素

int mSize; //当前有多少个元素

int* pAdress; //指向存储数据的空间

};

 

#endif

 

MyArray.cpp

#include"MyArray.h"

 

MyArray::MyArray(){

this->mCapacity = 100;

this->mSize = 0;

//在堆开辟空间

this->pAdress = new int[this->mCapacity];

}

//有参构造函数,用户指定容量初始化

MyArray::MyArray(int capacity){

this->mCapacity = capacity;

this->mSize = 0;

//在堆开辟空间

this->pAdress = new int[capacity];

}

//根据位置添加元素

void MyArray::SetData(int pos, int val){

if (pos < 0 || pos > mCapacity - 1){

return;

}

pAdress[pos] = val;

}

//获得指定位置数据

int MyArray::GetData(int pos){

return pAdress[pos];

}

//尾插法

void MyArray::PushBack(int val){

if (mSize >= mCapacity){

return;

}

this->pAdress[mSize] = val;

this->mSize++;

}

//获得长度

int MyArray::GetLength(){

return this->mSize;

}

//析构函数,释放数组空间

MyArray::~MyArray(){

if (this->pAdress != nullptr){

delete[] this->pAdress;

}

}

 

TestMyArray.cpp

#include"MyArray.h"

 

void test(){

//创建数组

MyArray myarray(50);

//数组中插入元素

for (int i = 0; i < 50; i++){

//尾插法

myarray.PushBack(i);

//myarray.SetData(i, i);

}

//打印数组中元素

for (int i = 0; i < myarray.GetLength(); i++){

cout << myarray.GetData(i) << " ";

}

cout << endl;

}

 

4.6 运算符重载

4.6.1 运算符重载基本概念

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

 

运算符重载(operator overloading)只是一种语法上的方便,也就是它只是另一种函数调用的方式。

在c++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字operator及其紧跟的运算符组成。差别仅此而已。它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。

语法:

   定义重载的运算符就像定义函数,只是该函数的名字是operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素。

  1. 运算符是一元(一个参数)的还是二元(两个参数);
  2. 运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是一个参数-此时该类的对象用作左耳参数)

 

 

   [两个极端]

   有些人很容易滥用运算符重载。它确实是一个有趣的工具。但是应该注意,它仅仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比我们写代码多多了)才有理由重载运算符。如果不是这样,就改用其他更易用,更易读的方式。

   对于运算符重载,另外一个常见的反应是恐慌:突然之间,C运算符的含义变得不同寻常了,一切都变了,所有C代码的功能都要改变!并非如此,对于内置的数据类型的表示总的所有运算符是不可能改变的

 

 

4.6.2 运算符重载碰上友元函数

友元函数是一个全局函数,和我们上例写的全局函数类似,只是友元函数可以访问某个类私有数据。

案例: 重载左移操作符(<<),使得cout可以输出对象。

class Person{

friend ostream& operator<<(ostream& os, Person& person);

public:

Person(int id,int age){

mID = id;

mAge = age;

}

private:

int mID;

int mAge;

};

 

ostream& operator<<(ostream& os, Person& person){

os << "ID:" << person.mID << " Age:" << person.mAge;

return os;

}

 

int main(){

 

Person person(1001, 30);

//cout << person; //cout.operator+(person)

cout << person << " | " << endl;

 

return EXIT_SUCCESS;

}

 

4.6.3 可重载的运算符

几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变运算符优先级不能改变运算符的参数个数。这样的限制有意义,否则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。

C++基础知识(四)-友元和运算符重载1

 

4.6.4 自增自减(++/--)运算符重载

重载的++和--运算符有点让人不知所措,因为我们总是希望能根据它们出现在所作用对象的前面还是后面来调用不同的函数。解决办法很简单,例如当编译器看到++a(前置++),它就调用operator++(a),当编译器看到a++(后置++),它就会去调用operator++(a,int).

 

class Complex{

friend ostream& operator<<(ostream& os,Complex& complex){

os << "A:" << complex.mA << " B:" << complex.mB << endl;

return os;

}

public:

Complex(){

mA = 0;

mB = 0;

}

//重载前置++

Complex& operator++(){

mA++;

mB++;

return *this;

}

//重载后置++

Complex operator++(int){

Complex temp;

temp.mA = this->mA;

temp.mB = this->mB;

mA++;

mB++;

return temp;

}

//前置--

Complex& operator--(){

mA--;

mB--;

return *this;

}

//后置--

Complex operator--(int){

Complex temp;

temp.mA = mA;

temp.mB = mB;

mA--;

mB--;

return temp;

}

void ShowComplex(){

cout << "A:" << mA << " B:" << mB << endl;

}

private:

int mA;

int mB;

};

 

void test(){

Complex complex;

complex++;

cout << complex;

++complex;

cout << complex;

 

Complex ret = complex++;

cout << ret;

cout << complex;

 

cout << "------" << endl;

ret--;

--ret;

cout << "ret:" << ret;

complex--;

--complex;

cout << "complex:" << complex;

}

 

优先使用++和--的标准形式,优先调用前置++。

如果定义了++c,也要定义c++,递增操作符比较麻烦,因为他们都有前缀和后缀形式,而两种语义略有不同。重载operator++和operator--时应该模仿他们对应的内置操作符。

对于++和--而言,后置形式是先返回,然后对象++或者--,返回的是对象的原值。前置形式,对象先++或--,返回当前对象,返回的是新对象。其标准形式为:

C++基础知识(四)-友元和运算符重载2

 

调用代码时候,要优先使用前缀形式,除非确实需要后缀形式返回的原值,前缀和后缀形式语义上是等价的,输入工作量也相当,只是效率经常会略高一些,由于前缀形式少创建了一个临时对象。

 

 

4.6.5 指针运算符(*、->)重载

class Person{

public:

Person(int param){

this->mParam = param;

}

void PrintPerson(){

cout << "Param:" << mParam << endl;

}

private:

int mParam;

};

 

class SmartPointer{

public:

SmartPointer(Person* person){

this->pPerson = person;

}

//重载指针的->、*操作符

Person* operator->(){

return pPerson;

}

Person& operator*(){

return *pPerson;

}

~SmartPointer(){

if (pPerson != NULL){

delete pPerson;

}

}

public:

Person* pPerson;

};

 

void test01(){

 

//Person* person = new Person(100);

//如果忘记释放,那么就会造成内存泄漏

 

SmartPointer pointer(new Person(100));

pointer->PrintPerson();

}

 

 

4.6.6 赋值(=)运算符重载

赋值符常常初学者的混淆。这是毫无疑问的,因为’=’在编程中是最基本的运算符,可以进行赋值操作,也能引起拷贝构造函数的调用。

class Person{

friend ostream& operator<<(ostream& os,const Person& person){

os << "ID:" << person.mID << " Age:" << person.mAge << endl;

return os;

}

public:

Person(int id,int age){

this->mID = id;

this->mAge = age;

}

//重载赋值运算符

Person& operator=(const Person& person){

this->mID = person.mID;

this->mAge = person.mAge;

return *this;

}

private:

int mID;

int mAge;

};

 

//1. =号混淆的地方

void test01(){

Person person1(10, 20);

Person person2 = person1; //调用拷贝构造

//如果一个对象还没有被创建,则必须初始化,也就是调用构造函数

//上述例子由于person2还没有初始化,所以会调用构造函数

//由于person2是从已有的person1来创建的,所以只有一个选择

//就是调用拷贝构造函数

person2 = person1; //调用operator=函数

//由于person2已经创建,不需要再调用构造函数,这时候调用的是重载的赋值运算符

}

//2. 赋值重载案例

void test02(){

Person person1(20, 20);

Person person2(30, 30);

cout << "person1:" << person1;

cout << "person2:" << person2;

person2 = person1;

cout << "person2:" << person2;

}

//常见错误,当准备给两个相同对象赋值时,应该首先检查一下这个对象是否对自身赋值了

//对于本例来讲,无论如何执行这些赋值运算都是无害的,但如果对类的实现进行修改,那么将会出现差异;

//3. 类中指针

class Person2{

friend ostream& operator<<(ostream& os, const Person2& person){

os << "Name:" << person.pName << " ID:" << person.mID << " Age:" << person.mAge << endl;

return os;

}

public:

Person2(char* name,int id, int age){

this->pName = new char[strlen(name) + 1];

strcpy(this->pName, name);

this->mID = id;

this->mAge = age;

}

#if 1

//重载赋值运算符

Person2& operator=(const Person2& person){

 

//注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存

//这个时候如果直接赋值,会导致内存没有及时释放

if (this->pName != NULL){

delete[] this->pName;

}

 

this->pName = new char[strlen(person.pName) + 1];

strcpy(this->pName,person.pName);

this->mID = person.mID;

this->mAge = person.mAge;

return *this;

}

#endif

//析构函数

~Person2(){

if (this->pName != NULL){

delete[] this->pName;

}

}

private:

char* pName;

int mID;

int mAge;

};

 

void test03(){

Person2 person1("John",20, 20);

Person2 person2("Edward",30, 30);

cout << "person1:" << person1;

cout << "person2:" << person2;

person2 = person1;

cout << "person2:" << person2;

}

 

 

如果没有重载赋值运算符,编译器会自动创建默认的赋值运算符重载函数。行为类似默认拷贝构造,进行简单值拷贝。

4.6.7 等于和不等于(==、!=)运算符重载

class Complex{

public:

Complex(char* name,int id,int age){

this->pName = new char[strlen(name) + 1];

strcpy(this->pName, name);

this->mID = id;

this->mAge = age;

}

//重载==号操作符

bool operator==(const Complex& complex){

if (strcmp(this->pName,complex.pName) == 0 && 

this->mID == complex.mID && 

this->mAge == complex.mAge){

return true;

}

return false;

}

//重载!=操作符

bool operator!=(const Complex& complex){

if (strcmp(this->pName, complex.pName) != 0 || 

this->mID != complex.mID || 

this->mAge != complex.mAge){

return true;

}

return false;

}

~Complex(){

if (this->pName != NULL){

delete[] this->pName;

}

}

private:

char* pName;

int mID;

int mAge;

};

void test(){

Complex complex1("aaa", 10, 20);

Complex complex2("bbb", 10, 20);

if (complex1 == complex2){ cout << "相等!" << endl; }

if (complex1 != complex2){ cout << "不相等!" << endl; }

}

 

4.6.8 函数调用符号()重载

class Complex{

public:

int Add(int x,int y){

return x + y;

}

int operator()(int x,int y){

return x + y;

}

};

void test01(){

Complex complex;

cout << complex.Add(10,20) << endl;

//对象当做函数来调用

cout << complex(10, 20) << endl;

}

 

4.6.9 不要重载&&、||

不能重载operator&& 和 operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的&&和||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了--而且能够保证不需要。我们都已经习惯这种方便的特性了。

我们说操作符重载其实是另一种形式的函数调用而已,对于函数调用总是在函数执行之前对所有参数进行求值。

class Complex{

public:

Complex(int flag){

this->flag = flag;

}

Complex& operator+=(Complex& complex){

this->flag = this->flag + complex.flag;

return *this;

}

bool operator&&(Complex& complex){

return this->flag && complex.flag;

}

public:

int flag;

};

int main(){

 

Complex complex1(0);  //flag 0

Complex complex2(1);  //flag 1

 

//原来情况,应该从左往右运算,左边为假,则退出运算,结果为假

//这边却是,先运算(complex1+complex2),导致,complex1的flag变为complex1+complex2的值, complex1.a = 1

// 1 && 1

//complex1.operator&&(complex1.operator+=(complex2))

if (complex1 && (complex1 += complex2)){   

cout << "真!" << endl;

}

else{

cout << "假!" << endl;

}

 

return EXIT_SUCCESS;

}

 

根据内置&&的执行顺序,我们发现这个案例中执行顺序并不是从左向右,而是先右猴左,这就是不满足我们习惯的特性了。由于complex1 += complex2先执行,导致complex1 本身发生了变化,初始值是0,现在经过+=运算变成1,1 && 1输出了真。

4.6.10 符号重载总结

  1. =, [], () 和 -> 操作符只能通过成员函数进行重载
  2. << 和 >>只能通过全局函数配合友元函数进行重载
  3. 不要重载 && 和 || 操作符,因为无法实现短路规则

常规建议

C++基础知识(四)-友元和运算符重载3

 

4.6.10 强化训练_字符串类封装

MyString.h

#define _CRT_SECURE_NO_WARNINGS

#pragma once

#include 

using namespace std;

 

class MyString

{

friend ostream& operator<< (ostream  & out, MyString& str);

friend istream& operator>>(istream& in, MyString& str);

 

public:

MyString(const char *);

MyString(const MyString&);

~MyString();

 

char& operator[](int index); //[]重载

 

//=号重载

MyString& operator=(const char * str);

MyString& operator=(const MyString& str);

 

//字符串拼接 重载+号

MyString operator+(const char * str );

MyString operator+(const MyString& str);

 

//字符串比较

bool operator== (const char * str);

bool operator== (const MyString& str);

private:

char * pString; //指向堆区空间

int m_Size; //字符串长度 不算'\0'

};

 

 

 

MyString.cpp

#include "MyString.h"

 

//左移运算符

ostream& operator<< (ostream & out, MyString& str)

{

out << str.pString;

return out;

}

//右移运算符

istream& operator>>(istream& in, MyString& str)

{

//先将原有的数据释放

if (str.pString != NULL)

{

delete[] str.pString;

str.pString = NULL;

}

char buf[1024]; //开辟临时的字符数组,保存用户输入内容

in >> buf;

 

str.pString = new char[strlen(buf) + 1];

strcpy(str.pString, buf);

str.m_Size = strlen(buf);

 

return in;

}

 

//构造函数

MyString::MyString(const char * str)

{

this->pString = new char[strlen(str) + 1];

strcpy(this->pString, str);

this->m_Size = strlen(str);

}

 

//拷贝构造

MyString::MyString(const MyString& str)

{

this->pString = new char[strlen(str.pString) + 1];

strcpy(this->pString, str.pString);

this->m_Size = str.m_Size;

}

//析构函数

MyString::~MyString()

{

if (this->pString!=NULL)

{

delete[]this->pString;

this->pString = NULL;

}

}

 

char& MyString::operator[](int index)

{

return this->pString[index];

}

 

MyString& MyString::operator=(const char * str)

{

if (this->pString != NULL){

delete[] this->pString;

this->pString = NULL;

}

this->pString = new char[strlen(str) + 1];

strcpy(this->pString, str);

this->m_Size = strlen(str);

return *this;

}

 

MyString& MyString::operator=(const MyString& str)

{

if (this->pString != NULL){

delete[] this->pString;

this->pString = NULL;

}

this->pString = new char[strlen(str.pString) + 1];

strcpy(this->pString, str.pString);

this->m_Size = str.m_Size;

return *this;

}

 

 

MyString MyString::operator+(const char * str)

{

int newsize = this->m_Size + strlen(str) + 1;

char *temp = new char[newsize];

memset(temp, 0, newsize);

strcat(temp, this->pString);

strcat(temp, str);

 

MyString newstring(temp);

delete[] temp;

 

return newstring;

}

 

MyString MyString::operator+(const MyString& str)

{

int newsize = this->m_Size + str.m_Size + 1;

char *temp = new char[newsize];

memset(temp, 0, newsize);

strcat(temp, this->pString);

strcat(temp, str.pString);

 

MyString newstring(temp);

delete[] temp;

return newstring;

}

 

bool MyString::operator==(const char * str)

{

if (strcmp(this->pString, str) == 0 && strlen(str) == this->m_Size){

return true;

}

 

return false;

}

 

bool MyString::operator==(const MyString& str)

{

if (strcmp(this->pString, str.pString) == 0 && str.m_Size == this->m_Size){

return true;

}

 

return false;

}

 

 

 

TestMyString.cpp

void test01()

{

MyString str("hello World");

 

cout << str << endl;

 

//cout << "请输入MyString类型字符串:" << endl;

//cin >> str;

 

//cout << "字符串为: " << str << endl;

 

//测试[]

cout << "MyString的第一个字符为:" << str[0] << endl;

 

//测试 =

MyString str2 = "^_^";

MyString str3 = "";

str3 = "aaaa";

str3 = str2;

cout << "str2 = " << str2 << endl;

cout << "str3 = " << str3 << endl;

 

//测试 +

MyString str4 = "我爱";

MyString str5 = "北京";

MyString str6 = str4 + str5;

MyString str7 = str6 + "天安门";

 

cout << str7 << endl;

 

//测试 ==

if (str6 == str7)

{

cout << "s6 与 s7相等" << endl;

}

else

{

cout << "s6 与 s7不相等" << endl;

}

}

 

4.6.11 附录:运算符和结合性

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

 

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

 

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<<

左移

变量<<表达式>

左到右

双目运算符

>>

右移

变量>>表达式

 

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

<

小于

表达式<表达式>

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

 

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

 

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

 

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

 

15

逗号运算符

表达式,表达式,…

左到右

--

 

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:VSCode配置PyQt5和designer

下一篇:记一次mybatis处理blob数据

您可能感兴趣

  • 突破C++的虚拟指针--C++程序的缓冲区溢出攻击

    作者:rix (rix@securiweb.net) backend注:本文来自Phrack56期的《SMASHING C++ VPTRS》。正如大多数国外黑客的文章,技术原理及应用都讲得比较详细,但所提供的源代码似乎总是会存在不大不小的问题。这也许是因为他们觉得应该让读者自己去研究和调试,以更好地掌握这些技术。或许以后我也会这样做。;)测试环境:  操作系统:Red Hat 6.1 (i3...

  • C/C++是程序员必须掌握的语言吗?

    滚滚长江东逝水,浪花淘尽英雄。虽说是个人英雄的时代已经成为过去,但我们仍然不能对这样的榜样们有所忘怀,他们是WPS求伯君、CCDOS严援朝、2.13吴晓军、四通利方王志东、CCED朱崇君、UCDOS鲍岳桥等。因为他们不仅是成名的优秀程序员,也不仅是在写文章时所必须想到的人物,更主要的是他们激荡了很多批程序员的编程热情,坚定了学会汇编和C就能走天下的决心和勇气,他们代表着以往中国软件业的辉煌。...

  • C/C++的思索 C++之父访谈录 [上帝的玩笑吗?]

    C/C++的思索 C++之父访谈录 作者:佚名 翻译整理:pigprince ---------------------------------------------------------------------- 在1998年的元旦,Bjarne Stroustrup(C++之父)接受了IEEE《计算机》杂志记者的专访。编辑很自然的认为他会对于过去七年来使用他创建的语言进行面对对象设计...

  • STL知识准备: 1. C++关键字typename

    /******************************************************************************** SGI*STL是STL之父Alexander Stepanov和STL巨匠Matt Austern等人的作品, 是当今 ** 最富盛名、最出色的STL实现版本,全部源代码和说明文档可从www.sgi.com/STL/下 ** 载,...

  • C++之父B. Stroustrup近期言论

    [译者按]  Bjarne Stroustrup博士,1950年出生于丹麦,先后毕业于丹麦阿鲁斯大学和英国剑桥大学,AT&T大规模程序设计研究部门负责人,AT&T、贝尔实验室和ACM成员。1979年,B. S开始开发一种语言,当时称为“C with Class”,后来演化为C++。1998年,ANSI/ISO C++标准建立,同年,B. S推出了其经典著作The C++ Programmin...

  • C++与OOP,谎言?现实?软件工程的尝试?

    真的,假的?触目惊心:C++之父访谈录公告正文    在1998年的元旦,Bjarne Stroustrup(C++之父)接受了IEEE《计算机》杂志记者的专访。编辑很自然的认为他会对于过去七年来使用他创建的语言进行面对对象设计做一个历史性的回顾。而在这个专访中,记者获得了更有价值的新闻,但是最后编辑决定为了整个IT产业,这个稿子不能发表,但是就像其它被砍掉的新闻,往往还是弄得路人皆知的。 ...

  • 详述C++语言的VxD与外界通讯的所有接口

    详述C++语言的VxD与外界通讯的所有接口作者:马文晓 一.什么是VxD? ---- 从多任务操作系统Windows 3.1起,计算机中的任一物理设备x可同时被基于Dos或Windows的多个进程使用,这种一对多的关系称为"设备虚拟化",各进程通过运行在核心层的VxD(虚拟x设备驱动程序)存取物理设备x.操作系统提供给用户的软件服务也可以用VxD实现.计算机中的其它资源,如CPU,内存等也可...

  • 在Visual C++中用ADO进行数据库编程

    在Visual C++中用ADO进行数据库编程     作者: 蒋东宇转自: www.vckbase.com  ActiveX数据对象(ADO)是OLE DB上面的高层数据库API。我们在C++程序中也可以调用ADO。本文将在VC 6.0环境下做一个小小的例子解释如何使用ADO。   1. 生成应用程序框架并初始化OLE/COM库环境   创建一个标准的MFC AppWizard(exe)...

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
C++基础知识(四)-友元和运算符重载介绍:华为云为您免费提供C++基础知识(四)-友元和运算符重载在博客、论坛、帮助中心等栏目的相关文章,同时还可以通过 站内搜索 查询更多C++基础知识(四)-友元和运算符重载的相关内容。| 移动地址: C++基础知识(四)-友元和运算符重载 | 写博客