C++中类的静态成员、存储、this、友元和运算符重载

news2024/12/24 3:10:01

静态成员

        在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static 声明为静态的,称为静态成员。
        不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个 类的对象共享。

静态成员变量

        在一个类中,若将一个成员变量声明为 static ,这种成员称为静态成员变量。与一 般的数据成员不同,无论建立了多少个对象,都只有一个静态数据的拷贝。静态成 员变量,属于某个类,所有对象共享。
        静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。
        静态成员变量必须在类中声明,在类外定义。
        静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占空间。
        静态数据成员可以通过类名或者对象名来引用。
class Person{
public:
    //类的静态成员属性
    static int sNum;
private:
    static int sOther;
};
    //类外初始化,初始化时不加 static
    int Person::sNum = 0;
    int Person::sOther = 0;
    int main(){
        //1. 通过类名直接访问
        Person::sNum = 100;
        cout << "Person::sNum:" << Person::sNum << endl;
        //2. 通过对象访问
        Person p1, p2;
        p1.sNum = 200;
        cout << "p1.sNum:" << p1.sNum << endl;
        cout << "p2.sNum:" << p2.sNum << endl;
        //3. 静态成员也有访问权限,类外不能访问私有成员
        //cout << "Person::sOther:" << Person::sOther << endl;
        Person p3;
        //cout << "p3.sOther:" << p3.sOther << endl;
        system("pause");
        return EXIT_SUCCESS;
}

静态成员函数

        在类定义中,前面有 static 说明的成员函数称为静态成员函数。静态成员函数使用 方式和静态变量一样,同样在对象没有创建前,即可通过类名调用。静态成员函数 主要为了访问静态变量,但是,不能访问普通成员变量。
        静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完 成对静态数据成员的封装。
        静态成员函数只能访问静态变量,不能访问普通成员变量
        静态成员函数的使用和静态成员变量一样
        静态成员函数也有访问权限
        普通成员函数可访问静态成员变量、也可以访问非经常成员变量
class Person{
public:
    //普通成员函数可以访问 static 和 non-static 成员属性
    void changeParam1(int param){
    mParam = param;
    sNum = param;
}
    //静态成员函数只能访问 static 成员属性
static void changeParam2(int param){
    //mParam = param; //无法访问
    sNum = param;
}
private:
    static void changeParam3(int param){
        //mParam = param; //无法访问
        sNum = param;
    }
public:
        int mParam;
        static int sNum;
};
    //静态成员属性类外初始化
    int Person::sNum = 0;
    int main(){
        //1. 类名直接调用
        Person::changeParam2(100);
        //2. 通过对象调用
        Person p;
        p.changeParam2(200);
        //3. 静态成员函数也有访问权限
        //Person::changeParam3(100); //类外无法访问私有静态成员函数
        //Person p1;
        //p1.changeParam3(200);
        return EXIT_SUCCESS;
}

静态成员属性

        如果一个类的成员,既要实现共享,又要实现不可改变,那就用 static const 修饰。 定义静态 const 数据成员时,最好在类内部初始化。
class Person{
public:
    //static const int mShare = 10;
    const static int mShare = 10; //只读区,不可修改
};
int main(){
    cout << Person::mShare << endl;
    //Person::mShare = 20;
    return EXIT_SUCCESS;
}

静态成员实现单例模式

        单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的 特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访 问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象 只能存在一个,单例模式是最好的解决方案。
Singleton (单例):在单例类的内部实现只生成一个实例,同时它提供一个静态 的 getInstance() 工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实 例化,将其默认构造函数和拷贝构造函数设计为私有;在单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。
        用单例模式,模拟公司员工使用打印机场景,打印机可以打印员工要输出的内容, 并且可以累积打印机使用次数。
class Printer{
public:
    static Printer* getInstance(){ return pPrinter;}
    void PrintText(string text){
        cout << "打印内容:" << text << endl;
        cout << "已打印次数:" << mTimes << endl;
        cout << "--------------" << endl;
        mTimes++;
}
private:
    Printer(){ mTimes = 0; }
    Printer(const Printer&){}
private:
    static Printer* pPrinter;
    int mTimes;
};
Printer* Printer::pPrinter = new Printer;
void test(){
    Printer* printer = Printer::getInstance();
    printer->PrintText("离职报告!");
    printer->PrintText("入职合同!");
    printer->PrintText("提交代码!");
}

成员变量和函数的存储

        在 c 语言中, 分开来声明的,也就是说,语言本身并没有支持 数据 函数 之间 的关联性我们把这种程序方法称为“ 程序性的 ,由一组 分布在各个以功能为导航 的函数中” 的算法驱动,它们处理的是共同的外部数据。
        c++实现了 封装 ,那么数据 ( 成员属性 ) 和操作 ( 成员函数 ) 是什么样的呢?
        “数据 处理数据的操作 ( 函数 )” 是分开存储的。
        c++中的非静态数据成员直接内含在类对象中,就像 c struct 一样。
        成员函数(member function) 虽然内含在 class 声明之内,却不出现在对象中。
        每一个非内联成员函数(non-inline member function) 只会诞生一份函数实例
class MyClass01{
public:
    int mA;
};
class MyClass02{
public:
    int mA;
    static int sB;
};
class MyClass03{
public:
    void printMyClass(){
        cout << "hello world!" << endl;
    }
public:
    int mA;
    static int sB;
};
class MyClass04{
public:
    void printMyClass(){
        cout << "hello world!" << endl;
    }
    static void ShowMyClass(){
        cout << "hello world!" << endl;
    }
public:
    int mA;
    static int sB;
};
int main(){
    MyClass01 mclass01;
    MyClass02 mclass02;
    MyClass03 mclass03;
    MyClass04 mclass04;
    cout << "MyClass01:" << sizeof(mclass01) << endl; //4
    //静态数据成员并不保存在类对象中
    cout << "MyClass02:" << sizeof(mclass02) << endl; //4
    //非静态成员函数不保存在类对象中
    cout << "MyClass03:" << sizeof(mclass03) << endl; //4
    //静态成员函数也不保存在类对象中
    cout << "MyClass04:" << sizeof(mclass04) << endl; //4
    return EXIT_SUCCESS;
}
通过上面的案例,我们可以的得出: C++ 类对象中的变量和函数是分开存储。

this 指针

this 指针工作原理

        通过上例我们知道,c++ 的数据和操作也是分开存储,并且每一个非内联成员函数 (non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象 会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
        c++通过提供特殊的对象指针, this 指针,解决上述问题。 this 指针指向被调用的 成员函数所属的对象。
        c++规定, this 指针是隐含在对象成员函数内的一种指针。当一个对象被创建后, 它的每一个成员函数都含有一个系统自动生成的隐含指针 this ,用以保存这个对象 的地址,也就是说虽然我们没有写上 this 指针,编译器在编译的时候也是会加上 的。因此 this 也称为 指向本对象的指针 this 指针并不是对象的一部分,不会影 响 sizeof( 对象 ) 的结果。
        this 指针是 C++ 实现封装的一种机制,它将对象和该对象调用的成员函数连接在 一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写 this , 而是让系统进行默认设置。
        this 指针永远指向当前对象。
        成员函数通过 this 指针即可知道操作的是那个对象的数据。 This 指针是一种隐含 指针,它隐含于每个类的非静态成员函数中。This 指针无需定义,直接使用即可。
        注意:静态成员函数内部没有 this 指针,静态成员函数不能操作非静态成员变量。
c++ 编译器对普通成员函数的内部处理 :

this 指针的使用

当形参和成员变量同名时,可用 this 指针来区分
在类的非静态成员函数中返回对象本身,可使用 return *this.
class Person{
public:
    //1. 当形参名和成员变量名一样时,this 指针可用来区分
    Person(string name,int age){
        //name = name;
        //age = age; //输出错误
        this->name = name;
        this->age = age;
    }
    //2. 返回对象本身的引用
    //重载赋值操作符
    //其实也是两个参数,其中隐藏了一个 this 指针
    Person PersonPlusPerson(Person& person){
        string newname = this->name + person.name;
        int newage = this->age + person.age;
        Person newperson(newname, newage);
        return newperson;
    }
    void ShowPerson(){
        cout << "Name:" << name << " Age:" << age << endl;
    }
public:
    string name;
    int age;
};
//3. 成员函数和全局函数(Perosn 对象相加)
Person PersonPlusPerson(Person& p1,Person& p2){
    string newname = p1.name + p2.name;
    int newage = p1.age + p2.age;
    Person newperson(newname,newage);
    return newperson;
}
int main(){
    Person person("John",100);
    person.ShowPerson();
    cout << "---------" << endl;
    Person person1("John",20);
    Person person2("001", 10);
    //1.全局函数实现两个对象相加
    Person person3 = PersonPlusPerson(person1, person2);
    person1.ShowPerson();
    person2.ShowPerson();
    person3.ShowPerson();
    //2. 成员函数实现两个对象相加
    Person person4 = person1.PersonPlusPerson(person2);
    person4.ShowPerson();
    system("pause");
    return EXIT_SUCCESS;
}

const 修饰成员函数

        用 const 修饰的成员函数时, const 修饰 this 指针指向的内存区域,成员函数体 内不可以修改本类中的任何普通成员变量, 当成员变量类型符前用 mutable 修饰时例外
//const 修饰成员函数
class Person{
public:
    Person(){
        this->mAge = 0;
        this->mID = 0;
    }
    //在函数括号后面加上 const,修饰成员变量不可修改,除了 mutable 变量
    void sonmeOperate() const{
        //this->mAge = 200; //mAge 不可修改
        this->mID = 10;
    }
    void ShowPerson(){
        cout << "ID:" << mID << " mAge:" << mAge << endl;
    }
private:
    int mAge;
    mutable int mID;
};
int main(){
    Person person;
    person.sonmeOperate();
    person.ShowPerson();
    system("pause");
    return EXIT_SUCCESS;
}

const 修饰对象(常对象)

常对象只能调用 const 的成员函数
常对象可访问 const 或非 const 数据成员,不能修改,除非成员用 mutable 修饰
class Person{
public:
    Person(){
        this->mAge = 0;
        this->mID = 0;
    }
    void ChangePerson() const{
        mAge = 100;
        mID = 100;
    }
    void ShowPerson(){
         this->mAge = 1000;
        cout << "ID:" << this->mID << " Age:" << this->mAge << endl;
    }
public:
    int mAge;
    mutable int mID;
};
void test(){
    const Person person;
    //1. 可访问数据成员
    cout << "Age:" << person.mAge << endl;
    //person.mAge = 300; //不可修改
    person.mID = 1001; //但是可以修改 mutable 修饰的成员变量
    //2. 只能访问 const 修饰的函数
    //person.ShowPerson();
    person.ChangePerson();
}

友元

        类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部( 作用域之外 ) 访问。 但是,有时候需要在类的外部访问类的私有成员,怎么办?
        解决方法是使用友元函数,友元函数是一种特权函数,c++ 允许这个特权函数访问 私有成员。这一点从现实生活中也可以很好的理解:
        比如你的家,有客厅,有你的卧室,那么你的客厅是 Public 的,所有来的客人都 可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允 许你的闺蜜好基友进去。
程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。

友元语法

        friend 关键字只出现在声明处
        其他类、类成员函数、全局函数都可声明为友元
        友元函数不是类的成员,不带 this 指针
        友元函数可访问对象任意成员属性,包括私有属性
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++ 设计的目的是为了实用性,而不是追 求理想的抽象。

运算符重载

运算符重载基本概念

        运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同 的数据类型。
        运算符重载(operator overloading) 只是一种 语法上的方便 ”, 也就是它只是另一种函 数调用的方式。
        在 c++ 中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义, 只是函数的名字由关键字 operator 及其紧跟的运算符组成。差别仅此而已。它像 任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。
语法:
        定义重载的运算符就像定义函数,只是该函数的名字是 operator@, 这里的 @ 代表 了被重载的运算符。函数的参数中参数个数取决于两个因素。
        运算符是一元( 一个参数 ) 的还是二元 ( 两个参数 )
        运算符被定义为全局函数( 对于一元是一个参数,对于二元是两个参数 ) 还是成员
        函数( 对于一元没有参数,对于二元是一个参数 - 此时该类的对象用作左耳参数 )
[ 两个极端 ]
        有些人很容易滥用运算符重载。它确实是一个有趣的工具。但是应该注意,它仅 仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有 在能使涉及类的代码更易写,尤其是更易读时( 请记住,读代码的机会比我们写代 码多多了) 才有理由重载运算符。如果不是这样,就改用其他更易用,更易读的方 式。
        对于运算符重载,另外一个常见的反应是恐慌:突然之间,C 运算符的含义变得 不同寻常了,一切都变了,所有 C 代码的功能都要改变!并非如此,对于内置的 数据类型的表达式的的运算符是不可能改变的。(例如想重载 int 类型数据的 + 号)

运算符重载碰上友元函数

        友元函数是一个全局函数,和我们上例写的全局函数类似,只是友元函数可以访问
某个类私有数据。
案例 : 重载左移操作符 (<<), 使得 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;
}

可重载的运算符

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

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

        重载的++ -- 运算符有点让人不知所措,因为我们总是希望能根据它们出现在所作 用对象的前面还是后面来调用不同的函数。解决办法很简单,例如当编译器看到 ++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-- 时应该模仿他们对应的 内置操作符。
        对于++ -- 而言,后置形式是先返回,然后对象 ++ 或者 -- ,返回的是对象的原值。 前置形式,对象先++ -- ,返回当前对象,返回的是新对象。其标准形式为 :
        调用代码时候,要优先使用前缀形式,除非确实需要后缀形式返回的原值,前缀和 后缀形式语义上是等价的,输入工作量也相当,只是效率经常会略高一些,由于前 缀形式少创建了一个临时对象。

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

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();
}

赋值(=)运算符重载

        赋值符常常初学者的混淆。这是毫无疑问的,因为’=’ 在编程中是最基本的运算符, 可以进行赋值操作,也能引起拷贝构造函数的调用。
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;
}
        如果没有重载赋值运算符,编译器会自动创建默认的赋值运算符重载函数。行为类 似默认拷贝构造,进行简单值拷贝。

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

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; }
}

函数调用符号()重载

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;
}

不要重载&&||

        不能重载 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)){ 
         //complex1.operator+=(complex2)
        cout << "真!" << endl;
    }
    else{
        cout << "假!" << endl;
    }
    return EXIT_SUCCESS;
}
        根据内置&& 的执行顺序,我们发现这个案例中执行顺序并不是从左向右,而是先 右猴左,这就是不满足我们习惯的特性了。由于 complex1 += complex2 先执行, 导致 complex1 本身发生了变化,初始值是 0 ,现在经过 += 运算变成 1,1 && 1 输出 了真。

 符号重载总结

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

字符串类封装

MyString.h

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <iostream>
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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1258995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搜索百度可以直接生成代码拉

先看效果图&#xff1a; 使用示例&#xff1a; 比如我要搜索“JS取一个数在两个数更近”的方法&#xff0c;直接搜“JS取一个数在两个数更近”&#xff0c;点击百度一下&#xff0c;就会出现想要的代码&#xff0c;如上图。

网站频频告警故障排查实录

故障描述 位于某Proxmox VE超融合集群上的一个网站频频报警&#xff0c;表现的形式是一会儿服务不可用&#xff0c;一会儿又恢复&#xff08;如下图所示&#xff09;&#xff0c;但同一集群上的其他Web站点未发现异常。 可能的原因 1&#xff09;出口带宽占满。 2&#xff09;…

【技巧】Excel表格如何退出“只读方式”?

如果Excel表格被设置了“只读模式”&#xff0c;那每次打开Excel都会出现对话框提示是否以“只读方式”打开&#xff0c;并且以“只读方式”打开的Excel&#xff0c;如果进行更改是无法保存原文件的。那要如何退出“只读方式”呢&#xff1f; 首先&#xff0c;我们要看下Excel表…

【数据结构】顺序表---C语言版

【数据结构】顺序表 前言&#xff1a;一、线性表二、顺序表1.顺序表的概念及结构&#xff1a;2.顺序表的分类&#xff1a;3.顺序表缺陷&#xff1a; 三、顺序表的代码实现&#xff1a;1.头文件&#xff1a;2.函数文件&#xff1a;3.测试文件&#xff1a; 四、顺序表的相关OJ题&…

私域成交的方式---朋友圈成交

朋友圈不止是生活&#xff0c;也是工作营销的很好场所。朋友圈成交也就是私域成交中养客户成交。和之前的微商相似&#xff0c;微商就是在朋友圈发内容&#xff0c;引起客户的兴趣&#xff0c;再转到1对1私聊进行成交。 适合产品&#xff1a;所有的产品都适合&#xff0c;不管…

Python实现艺术设计?提取图片中颜色并绘制成可视化图表,从大师作品中提取配色方案

文章目录 导入模块并加载图片提取颜色并整合成表格绘制图表实战环节关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠…

android基于UDP实现聊天小功能

一、DatagramSocket DatagramSocket 是 Java 中用于发送和接收 UDP 数据包的类。它提供了创建和管理 UDP 套接字的功能。以下是 DatagramSocket 类的一些常用方法&#xff1a; DatagramSocket(): 创建一个未绑定到特定本地地址和端口的 DatagramSocket 实例。 DatagramSocket…

普乐蛙绵阳科博会一场VR科普航天科学盛宴科普知识

普乐蛙绵阳科普展&#xff1a;一场科学盛宴&#xff0c;点燃孩子探索欲望的火花! 普乐蛙绵阳科普展正在如火如荼地进行中&#xff0c;吸引了无数孩子和家长的热情参与。这场科普盛宴以独特的内外视角&#xff0c;让人们感受到科学的魅力&#xff0c;激发了孩子们对知识的渴望和…

蚁剑低版本反制

蚁剑低版本反制 漏洞概述 中国蚁剑是一款开源的跨平台网站管理工具&#xff0c;它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员。影响范围 AntSword <2.0.7 蚁剑实验版本&#xff1a;2.0.7 环境搭建&#xff1a; 172.16.1.233&#xff08;蓝队服…

如何在 Photoshop 中制作金像

如何使用渐变贴图和一些鲜为人知的 Photoshop 图像效果在 Photoshop 中制作金像 1. 如何为图像添加噪点 步骤 1 首先&#xff0c;将“颜色填充”图层设置为淡蓝色 #a9c9dd 颜色。 步骤 2 接下来&#xff0c;让我们右键单击>创建智能对象&#xff0c;以防万一我们以后想要…

【EasyExcel】导出excel并支持自定义设置数据行背景颜色等

需求背景&#xff1a; 根据查询条件将列表数据导出&#xff0c;并筛选出满足某个条件的数据&#xff0c;将满足条件的数据的背景颜色设置成黄色。 &#xff08;本文例子如&#xff1a;name出现的次数大于等于2&#xff0c;将相关数据背景颜色都设置为黄色&#xff09; …

代码签名证书是如何保护软件?

随着互联网的普及和技术的发展&#xff0c;软件开发已经成为了一个非常重要的行业。然而&#xff0c;软件安全问题也日益凸显&#xff0c;恶意软件、病毒、木马等威胁着用户的数据安全和隐私。为了确保软件的安全和可靠性&#xff0c;开发者需要采取一系列措施来保护他们的产品…

在线教育行业内卷突围,持续激活平台用户体验是关键

在线教育并不等同于K12教育。 众所周知&#xff0c;越来越卷的考试制度&#xff0c;已经被家长、学生们的谩骂和吐槽淹没了好几层。各种减负、杜绝课后补课等条例纷纷出台&#xff0c;不断的挤压着K12教育企业的生存空间。 于是乎&#xff0c;大家都认为&#xff0c;在线教育…

笔记62:注意力汇聚 --- Nadaraya_Watson 核回归

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a

一键换肤功能

使用css3的css变量来实现 示例&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&qu…

好用的IDEA插件推荐

前言 Idea 是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序,Idea 还具有许多插件和扩展&#xff0c;可以根据开发人员的需要进行定制和扩展&#xff0c;从而提高开发效率,今天我们就来介绍一款…

使用opencv的matchTemplate进行银行卡卡号识别

![字体文件](https://img-blog.csdnimg.cn/3a16c87cf4d34aceb0778c4b20ddadb2.png#pic_center import cv2 import numpy as npdef show_img(img, name"temp"):img cv2.resize(img, (0, 0), fx3, fy3)cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()de…

Windows10-用户账户控制、Windows远程桌面

Windows10用户账户控制怎么设置白名单 问题引出&#xff1a; 安装低版本搜狗输入法后经常弹出用户账户控制 解决方案&#xff1a; 全局模式&#xff1a; UAC控制最早出现在Windows Vista中&#xff0c;用户帐户控制&#xff08;UAC&#xff09;是一项旨在防止对您的计算机…

2024年进入-1/12的世界

1.从一段与AI的对话开始 cj: 1234...无限的&#xff0b;下去结果是无穷大吗&#xff1f; chatgpt: 是的&#xff0c;从常规的数学观点来看&#xff0c;级数 1234… 无限相加的结果是正无穷。 这是因为每一项都是正数&#xff0c;并且随着项数的增加&#xff0c;部分和会趋近…

校园虚拟化部署与横向扩展统一存储

项目背景 这所隶属教育部直属重点大学&#xff0c;学校设有11个学科体系&#xff0c;现有本硕博学生共29000余人&#xff0c;为积极响应“中国教育现代化2023战略部署”&#xff0c;校方制定教育信息化2.0发展目标&#xff0c;通过平台融合&#xff0c;数据驱动、技术赋能等措…