【C++笔记】十一、类的高级技术

1.操作符重载:赋予某一类型,可以使用某一操作符的能力

#include <iostream>
using namespace std;
class MyClass
{
private:
    int mValue;
public:
    int getValue();  // 声明方法 
    MyClass(int value);  // 声明构造方法
};

int MyClass::getValue()
{
    return mValue;
}

MyClass::MyClass(int value)
{
    mValue = value;
}

int main(int argc, const char * argv[]) {

    // MyClass  /*myClass1.*/operator+(MyClass myClass2) {this}
    int a = 20;
    int b = 30;
    int c = a + b;
    
    MyClass myClass1(20);
    MyClass myClass2(30);
    
    MyClass myClass = myClass1 + myClass2; // 会报错,C++不支持这两个对象相加

    return 0;
}

操作符重载语法:MyClass myClass1.operator+(MyClass myClass2),返回值类型是MyClass,myClass1是类本身,operator+看作类的方法,myClass2看作方法的参数,在方法operator+里面。通过指针this表示myClass1,依据此语法,实现操作符重载,在声明时,该语法写为:MyClass operator+(MyClass myClass2)则,方法operator+的写法:

MyClass MyClass::operator+(MyClass c)
{
    MyClass myClass(0);
    myClass.mValue = mValue + c.mValue;  // 式中的mValue可以写为this.mValue
    return myClass;
}

//实现了 MyClass myClass = myClass1 + myClass2

方法operator-

MyClass MyClass::operator-(MyClass c)
{
    MyClass myClass(0);
    myClass.mValue = mValue - c.mValue;
    return myClass;
}

//实现了 MyClass myClass = myClass1 - myClass2

MyClass myClass = myClass1 + myClass2 + myClass1 的实现:

第一步:myClass1.operator+(myClass2 + myClass1)

第二步:myClass1.operator+(myClass2.operator+(myClass1))

2.函数形式的操作符重载

//使用函数形式完成“*”的操作符重载/

#include <iostream>
using namespace std;

class MyClass
{
private:
    int mValue;
public:
    int getValue();
    void setValue(int value);
    MyClass(int value);
    MyClass operator+(MyClass c);
    MyClass operator-(MyClass c);
};

int MyClass::getValue()
{
    return mValue;
}

void MyClass::setValue(int value)
{
    mValue = value;
}

// 此处定义的不是类的方法,而是函数,所以就不能用this指针,那么传入参数就应该是两个,即乘法的左侧和右侧
MyClass operator*(MyClass &left, MyClass &right) // 加上符号“&”,表示以引用的方式传参,这样避免的值的直接传递,效率更高
{
    MyClass myClass(0);
    myClass.setValue(left.getValue() * right.getValue());
    return myClass;
}

MyClass MyClass::operator+(MyClass c)
{
    MyClass myClass(0);
    myClass.mValue = mValue + c.mValue;
    return myClass;
}

int main(int argc, const char * argv[]) {    
    //  函数形式的操作符重载,其中operator是关键字,所以operator+就可以用+代替    

    MyClass myClass1(20);
    MyClass myClass2(30);
    
    MyClass myClass = myClass1 + myClass2 + myClass1;
    cout << myClass.getValue() << endl;     // 70

    myClass = myClass1 - myClass2;
    cout << myClass.getValue() << endl;     // -10
    
    myClass = myClass + myClass1 * myClass2;
    cout << myClass.getValue() << endl;     //  590,优先级不变
    return 0;
}

3.操作符重载的限制

    操作符重载的限制
     1. 重载后的操作数至少有一个是用户自定义类型,为了防止用户重载基本类型的操作符
     2. 不能改变操作符本身的特性,例如,不能将二元操作符变成一元或三元操作符
     3. 不能修改操作符的优先级(先乘除后加减)
     4. 不能创建新的操作符
     5. 并不是所有的操作符都可以重载,例如sizeof、指针(*),?:(条件操作符)等
     6. 大多数可以重载的操作符都可以使用成员方法和非成员方法(函数形式)重载,但,赋值:=,函数调用:(),下标运算符:[ ],通过指针访问成员:->   只能通过成员方法重载,成员方法:在类、结构体、共用体中进行重载。

#include <iostream>
using namespace std;
struct MyStruct
{
    int value = 20;
  /*  int operator%(int v)  // 限制2,参数只能有一个,一个参数表示二元操作符
    {
        return value % v;
    }*/
    void operator=(int v)
    {
        value = v;
    }
};

int operator+(int left, MyStruct &myStruct) // 限制1
{
    return left + myStruct.value;
}

int operator%(MyStruct &myStruct, int v)  // 限制2,函数方法重载(非成员方法),用两个参数表示二元操作符
{
    return myStruct.value % v;
}

/*void operator=(MyStruct &myStruct, int v)
{
    myStruct.value = v;
}*/

int main(int argc, const char * argv[])
{
    int a = 1+2;
    MyStruct myStruct;
    int x = 30 + myStruct;
    cout << x << endl;   // 50

    cout << myStruct % 3 << endl;
    //int a = myStruct +&^%$ 4;    限制4

    myStruct = 40;
    cout << myStruct.value << endl;

    return 0;
}

4.友元函数

友元函数,一个非成员方法,这个函数可以使用类/结构体中的私有成员。
      1. 在类(结构体)中需要定义友元函数的原型(前面加friend)
      2. friend只能在友元函数的原型加,不能在友元函数的实现部分加friend
      3. 尽管友元函数的原型在类中定义,但不能像调用类成员方法一样调用友元函数

#include <iostream>
using namespace std;
class MyClass
{
private:
    int mValue;
public:
    MyClass(int value)
    {
        mValue = value;
    }
    
    void print()
    {
        cout << mValue << endl;
    }

    MyClass operator*(int right)  // 这种方式重载操作符有一个缺点:MyClass只能在乘号左侧
    {
        return MyClass(mValue * right); 
    }

    friend MyClass operator*(int, MyClass); // 用friend关键字声明函数原型,此时非成员方法也能访问私有成员mValue了
};

MyClass operator*(int left, MyClass right)  // 实现操作符重载,同时MyClass在乘号右侧
{
    return left * right.mValue;   // 使用friend声明后,可以访问私有成员mValue
}

int main(int argc, const char * argv[]) {

    MyClass result = 20 * MyClass(30) ;//这是MyClass在右侧的情况,此时按照参数类型,会先使用非成员方法,在非成员方法中,将两个参数位置互换,这样就符合了成员方法,进而使用成员方法进行计算
    result.print();
    
    return 0;
}

5.友元函数与<<操作符重载

#include <iostream>
using namespace std;
class MyClass
{
private:
    int mValue;
public:
    MyClass(int value)
    {
        mValue = value;
    }
    friend ostream& operator<<(ostream &os, MyClass &myClass);
};

ostream& operator<<(ostream &os, MyClass &myClass) // ostream:输出流
{
    return os << myClass.mValue;
}

int main(int argc, const char * argv[]) {

    // 友元函数与<<操作符重载
    MyClass myClass(20);
    cout << myClass << "abc" << endl;  // 对于操作符<<,myClass一定是右操作数,在成员方法中myClass必须是左操作数,所以需要用非成员方法对其进行重载

    /*
        ((cout << myClass) << "abc") << endl  // (cout << myClass)表达式的最终值应该是ostream类型,这样才能继续对右边的“abc”进行操作。所以operator<<的返回值应该是ostream&
    */

    return 0;
}

6.类型转换(数值和对象之间的互转)

  语法: operator typename( ) {... }
      1. 转换函数必须是方法,不能是函数
      2. 转换函数不能有返回值
      3. 转换函数不能有参数

#include <iostream>
using namespace std;

class MyClass
{
private:
    int mValue;
    double mDValue;
public:
    MyClass(int value)
    {
        mValue = value;
    }
    MyClass(double value)
    {
        mDValue = value;
    }
    operator int();
    operator double()
    {
        return mDValue;
    }
};

MyClass::operator int()
{
    return mValue;
}

int main(int argc, const char * argv[]) {

    // 类型转换(数值和对象之间的互转)
    //  语法: operator typename() {... }
    //  1. 转换函数必须是方法,不能是函数
    //  2. 转换函数不能有返回值
    //  3. 转换函数不能有参数

    int n = 33.3;
    MyClass myClass(30);  // 将int类型的值转换为MyClass对象
    int value = myClass;  // 会自动调用operator int()
    cout << value << endl;
    
    MyClass myClass1(30.123);
    double value1 = myClass1;  // 会自动调用operator double()
    cout << value1 << endl;
    return 0;
}

7.类中静态变量和静态常量的初始化问题

main.cpp

#include <iostream>
#include "Test.h"
using namespace std;

int main(int argc, const char * argv[])
{
    // 类中的静态变量
    MyClass myClass;   // 实例化类时,会执行类的构造方法,value变为200
    cout << MyClass::value << endl;   // 200
    //  1.  static变量不能直接在class中初始化
    //  2.  static变量不能在头文件中初始化
    //  3.  static变量可以在cpp文件中初始化,但初始化时不能指定static 
    //  4.  static变量可以在类的构造方法中初始化
    
    //  例外:类中的静态常量(static const)(必须是整型或枚举),可以在类中声明常量的同时初始化,也可以在cpp中初始化  
    return 0;
}

Test.cpp

#include "Test.h"
int MyClass::value = 100; // 对类中的静态成员进行初始化
// const int MyClass::max = 4321;
int MyClass::getValue()
{
    return value;
}

MyClass::MyClass()  // 类的构造方法
{
    value = 200;
}

Test.h

#ifndef __class_static__Test__
#define __class_static__Test__

class MyClass  // 这里并没有给类分配任何的内存空间,创建这个类的对象时,才会进行分配
{
public:
    static int value;  // 对于这个静态变量,如果在类中进行初始化,那系统就会给静态变量分配内存空间,这样的话就和类冲突了,所以不能用static int value = 10;
// 初始化静态的类的成员,需要在类的外部
    static const int max = 4321;  // 静态常量是例外
    MyClass();
    int getValue();
};

#endif /* defined(__class_static__Test__) */

8.复制构造方法

#include <iostream>
using namespace std;
class MyClass
{
private:
    int mValue = 0;
public:
    MyClass(int value)
    {
        mValue = value;
    }

    MyClass(const MyClass& myClass); // 复制构造方法

    int getValue()
    {
        return mValue;
    }

    void setValue(int value)
    {
        mValue = value;
    }
};
// 复制构造方法,如果用户自定义了复制构造方法,那么在类之间的赋值时,会调用自定义构造方法,而不是默认的
MyClass::MyClass(const MyClass& myClass)  // 如果设置成内联的,那么赋值的等号右边作为参数传入
{
    mValue = myClass.mValue;
    cout << myClass.mValue << endl;
}

int main(int argc, const char * argv[])
{
    // 复制构造方法
    MyClass myClass(20);
    MyClass newClass = myClass;  // clone,复制当前的对象,连同里面的值,无论里面的是私有的还是公共的
    cout << newClass.getValue() << endl;    // 打印 20
    
    newClass.setValue(100);
    cout << myClass.getValue() << endl;   // 20
    cout << newClass.getValue() << endl;  // 100
    
    cout << "-------" << endl;

    // 触发复制构造方法的四种情况    
    // 1
    MyClass myClass1(10);
    MyClass newMyClass1(myClass1);  // 打印 10 ,newMyClass1和myClass1是类创建的对象
    
    //  2
    MyClass myClass2(30);
    MyClass newMyClass2 = myClass2; // 打印 30
    
    // 3
    MyClass myClass3(50);
    MyClass newMyClass3 = MyClass(myClass3); // 50 MyClass是复制构造方法,和构造方法的区别是其以类对象作为传入参数
    
    // 4
    MyClass myClass4(60);
    MyClass *pMyClass = new MyClass(myClass4);  // 60
    
    return 0;
}

9.使用复制构造方法可能会带来的问题

#include <iostream>
using namespace std;
class MyClass
{
private:
    string mName;
    char *mProductName;
public:
    MyClass()
    {
        mName = "Bill Gates";
        mProductName = new char[20];
        strcpy(mProductName, "Surface Pro3");
    }

    string getName()
    {
        return mName;
    }
    
    char* getProductName()
    {
        return mProductName;
    }

    ~MyClass()  // 析构方法
    {
        delete [] mProductName;
    }
    
    MyClass(const MyClass& myClass) // 自定义的复制构造方法
    {
        mName = myClass.mName;
      // error:  mProductName = myClass.mProductName;
        mProductName = new char[20];
        strcpy(mProductName, myClass.mProductName);
    }
};

int main(int argc, const char * argv[]) {
    //  复制构造方法可能带来的问题
    MyClass *pMyClass1 = new MyClass();
    MyClass *pMyClass2 = new MyClass(*pMyClass1);  // 这里的*是取地址值,即类对象

// delete pMyClass1; 此句会执行析构方法,那么下面的第一句就不会再打印值了,都给释放了,
//想让它打印值,就需要自定义复制构造方法,让指针所指向的量,按照值去传递;如果不自定义复制构造方法,
//那么就会调用默认的方法,默认的方法传递指针值时,是通过多个指针指向相同地址实现的,
//并不是通过值传递,这样的话,一旦内存被释放,就会没值了

    cout << pMyClass2->getProductName() << endl;  
    cout << pMyClass2->getName() << endl;       
    
    return 0;
}

10.类的静态成员方法

#include <iostream>
using namespace std;
class MyClass
{
public:
    static int code;
    static string getName()
    {
        return "Bill Gates";
    }
    static int getCode();
    
};

int MyClass::code = 20;  // 类中的静态成员不能在类内初始化
int MyClass::getCode()  // 在外部定义时,静态方法和普通方法看不出差别
{
    return code;
}

int main(int argc, const char * argv[]) {
    // 类中的静态成员方法
    cout << MyClass::code << endl;  // 对于静态成员来说,不需要对类进行实例化,直接打印值就能打印出来,对于类中的普通成员可以按占位符理解,不实例化是打印不出来值的
    cout << MyClass::getName() << endl;
    
    return 0;
}

11.函数/方法的参数和返回值应该返回对象,还是对象引用

  方法/函数如何返回对象
     1.  直接返回对象,如果对象很大,那么会消耗很多资源
     2.  返回对象的引用,尽量不要返回函数内局部变量的引用
     3.  返回对象的指针

#include <iostream>
using namespace std;

class MyClass
{
public:
    int code = 20;
    MyClass(const MyClass& myClass)  // 参数类型是引用
    {
        code = myClass.code;
        cout << "MyClass(const MyClass& myClass)" << endl;
    }
    MyClass()
    {
        
    }
};

void process(MyClass myClass)
{
    
}

MyClass mul2(MyClass myClass)
{
    myClass.code = myClass.code * 2;
    return myClass;
}

MyClass mul2_1(MyClass &myClass)
{
    myClass.code = myClass.code * 2;
    return myClass;
}

MyClass& mul2_2(MyClass &myClass)
{
    myClass.code = myClass.code * 2;
    return myClass;
}

int main(int argc, const char * argv[]) {

    MyClass myClass1;
    // MyClass myClass2 = mul2(myClass1); // 调用了两次复制构造方法,第一次是myClass1作为参数传入函数栈时,进行了复制,第二次等号赋值产生的
    process(mul2(myClass1));
    // MyClass myClass2 = mul2_1(myClass1); // 调用了一次复制构造方法,等号赋值产生的
    //  MyClass &myClass2 = mul2_2(myClass1);  // 一次构造方法也没调用
    //  cout << myClass2.code << endl;
    
    return 0;
}

12.操作符重载返回常量对象引用,还是对象引用

#include <iostream>
using namespace std;
class MyClass
{
public:
    int code;
    MyClass(int code)
    {
        this->code = code;
    }
    
    const MyClass& operator+(MyClass &myClass)  // 返回值是常量引用,那么所返回的对象是不能修改的
    {
        myClass.code = code + myClass.code;
        return myClass;
    }
};
int main(int argc, const char * argv[]) {
    // 操作符重载返回常量对象引用,还是对象引用
    MyClass myClass1(20);
    MyClass myClass2(30);
    
    MyClass myClass = myClass1 + myClass2;  // 加号重载后,二者相加所返回的值是myClass2,即,将myClass2赋值给了myClass,myClass2里面是50
    
    cout << myClass.code << endl;
  //  cout << myClass2.code << endl;   //  50
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DUANDAUNNN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值