04-C++ 类和对象-02

类和对象-02

1. this 指针

1.1 概念:

  • 谁调用this所在的函数,this就存储谁的地址,即指向谁 。

1.2 特点:

  1. 在当前类的非静态成员函数中调用本类非静态成员时,默认有this关键字
  2. 静态成员函数,没有this指针。

1.3 使用场景

  • 局部变量与成员变量重名时,使用this区分
  • 调用本类其他成员,此时this可以省略不写

1.4 示例

#include 
#include
using namespace std;
class A{
private:
    int x;
public:
    A(){}
    A(int a):x(a){}
    void test01()
    {
        cout << x << endl;
    }
    void test02();
};
void A::test02()
{
    //谁调用this所在的函数,this就指向谁
    //2,在本类函数中调用本类其他成员,此时可以忽略this不写,此时下面两行代码相等
    //cout << this->x << endl;
    cout << x << endl;
}

class Person{
private:
    char name[50];
    int age;
public:
    Person(char *name,int age)
    {
        //1,当局部变量与成员变量重名时,用于区分
        strcpy(this->name,name);
        this->age = age;
    }
    static void test()
    {
        //静态函数中没有this关键字,假如有this,用类名调用静态函数时是没有指向的
        //静态函数中也不能使用this关键字
        //cout << this->name << endl;
    }
};

int main(int argc, char *argv[])
{
    A a(10);
    a.test02(); //10

    A a2(20);
    a2.test02(); //20
    return 0;
}

2. const修饰成员函数(了解)

概念:

const修饰的成员函数内部不能对成员数据写操作,mutable修饰的成员数据 除外。

示例:

#include 
#include
using namespace std;
class B{
private:
    int x;
    mutable int a;
public:
    B(){

    }
    B(int x){
        this->x = x;
    }
    void test() const
    {
        //const修饰的成员函数中,不能修改该类的其他成员变量的值
        //x = 10;
        // 可以读取该类的成员变量的值
        cout << x << endl;
        //const修饰的函数中可以修改mutable修饰的成员变量的值
        a = 100;
    }
};
int main(int argc, char *argv[])
{
    B b(1);
    b.test();
    return 0;
}

3. 友元(重要)

3.1 概述

关键字:friend

可以声明:

  1. 全局函数
  2. 成员函数

注意:

  • 友元打破c++的封装性。一般用于运算符重载

3.2 全局友元函数

特点:

  • 可以访问其友元类的任意成员,包括私有成员

步骤:

  1. 定义并实例全局函数
  2. 类中声明 步骤1中的函数为友元函数(这个函数是哪个类的友元函数,就在哪个类中声明)
  3. 步骤1中定义的函数,可以访问步骤2中定义的类中的所有成员

示例:

#include 
using namespace std;
class A{
    //声明全局函数fun01为A类的友元函数
    friend void fun01();
private:
    int x;
public:
    A(int x)
    {
        this->x = x;
    }
};

void fun01()
{
    A a(10);
    //访问A类中的x
    cout << a.x << endl;
}

int main(int argc, char *argv[])
{
    fun01();	//10
    return 0;
}

3.3 成员友元函数

特点:

  • 可以访问其友元类的任意成员,包括私有成员

步骤:

  1. 定义B类,其中函数只定义不实现
  2. 定义并实现A类,在其中声明B类的成员函数testB为友元类
  3. 实现B类的成员函数,其中可以访问A的任意成员

注意:

  • 成员函数作为友元 那么成员函数所在的类 必须定义到最上方
  • 成员函数所在的类的所有成员函数 必须在两个类的下方实现

示例:

#include 

using namespace std;
//定义B类,其中函数只定义不实现
class B{
public:
    void testB();
};
//定义并实现A类,在其中声明成员函数testB为友元函数
class A{
    //声明B类中的成员函数testB为A类的友元函数
    friend void B::testB();
private:
    int x;
public:
    A(int x)
    {
        this->x = x;
    }
};

//实现B类的成员函数
void B::testB()
{
    A a(10);
    cout << a.x << endl;
}

int main(int argc, char *argv[])
{
    B b;
    b.testB();  //10

    return 0;
}

3.4 整个类作为友元

特点:

在B中声明A为B的友元类,此时A中任意成员函数中皆可直接访问B中的成员

步骤:

  1. 定义C类
  2. 定义并实现A类,在其中声明C类为 友元类
  3. 实现C类,C类中所有函数都是A类的友元函数,可以访问A类中的任意成员

示例:

#include 

using namespace std;

//声明C类
class C;
//定义并实现A类,在其中声明成员函数testB为友元函数
class A{
    //声明C类为A类的友元类
    //此时C类中所有函数都是A类的友元函数
    friend class C;
    //友元函数中可以访问其对应的友元类中的任意成员,包含私有成员
private:
    int x;
public:
    A(int x)
    {
        this->x = x;
    }
};

class C{
public:
    void testC01(A& a){
        cout << a.x << endl;
    }
    void testC02(A& a){
        cout << a.x << endl;
    }
};

int main(int argc, char *argv[])
{
    C c;
    A a(20);
    c.testC01(a); //20
    A a2(100);
    c.testC02(a2); //100
    return 0;
}

3.5 注意

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

4. string

作用:c++字符串类,使其字符串操作方便

可以当做数据类型来用。

示例:

#include 
#include 
#include 
using namespace std;

int main(int argc, char *argv[])
{
    //c语言定义字符串的方式
    //char str01[50] = "hello world";
    //char *str02 = "hello world";

    string str01 = "hello world";
    //1、输出字符串
    cout << str01 << endl;  //hello world

//    string str02;
//    //2、输入字符串
//    cin >> str02;
//    cout << str02 << endl;

    //3、字符串赋值,使用深拷贝
    string str03 = str01;
    cout << "str03:" << str03 << endl;  //str03:hello world

    str01 = "hello";

    cout << "str03:" << str03 << endl; //str03:hello world

//    c拼接字符串
//    char str11[50] = "hello";
//    char str22[10] = "world";
//    strcat(str11,str22);
//    cout << str11 << endl;

    string str04 = "hello";
    string str05 = "world";
    //4、c++字符串拼接
    str04 = str04 + str05;
    cout << "str04:" <<str04 << endl; //str04:helloworld

    //c比较字符串是否相同
    //0,相同
    //非0不同
//    char str11[50] = "hello";
//    char str22[10] = "world";
//    int i = strcmp(str11,str22);
//    cout << "i =" << i << endl;

    //5、c++字符串比较
    //返回值bool型
    //0,false; 1,true
    cout << (str04 == str05) << endl; //0
    string str06 = "helloworld";
    cout << (str04 == str06) << endl; //1
    return 0;
}

5. 运算符重载

5.1 引入

经源码查看string发现其也是一个类

那么为什么string的类对象可以使用>>,<<,+,==等运算符,我们自定义的类不行呢?

因为string类对运算符进行了重载

那我们如何实现运算符的重载?

5.2 概述

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

关键字:operator

语法:

返回值类型 operator 运算符(形参列表)
{
	函数体
} 

如:
	>>
    void operator >>(形参列表)
    { }

思路:

1、分析 运算符的运算对象的个数

2、分析 运算符左边 运算对象自定对象 还是 其他

  • 左边:是 其他 只能全局函数实现
  • 左边:自定义对象 (必须使用友元)
    • 可以用使用 **全局函数 ** 重载运算符(参数个数 和 运算符对象的个数一致)
    • 也可以使用 成员函数 重载运算符(参数可以少一个) (推荐)

可以重载的运算符:

注意:尽量不要重载&& ||,无法实现&& || 短路 。

5.3 重载<<,>>运算符

效果:

通过<<输出自定义类型的变量
或
通过>>输入自定义类型变量

分析:

<< 、>>符号左边为 coutcin 不是自定义对象,只能使用全局函数对其进行重载

示例:

 #include 
#include 
#include 
using namespace std;
class Stu{
    //输出、输入全局友元函数声明
	friend ostream& operator <<(ostream& out,Stu& stu);
	friend istream& operator >>(istream& in,Stu& stu);
private:
    char name[50];
    char sex[10];
    int age;
public:
    Stu(){}
    Stu(char *name,char *sex,int age)
    {
        strcpy(this->name,name);
        strcpy(this->sex,sex);
        this->age = age;
    }
    Stu(const Stu& stu)
    {
        strcpy(this->name,stu.name);
        strcpy(this->sex,stu.sex);
        this->age = stu.age;
    }
};
//1参:符号左边的变量
//2参:符号右边的变量
//参数传递时防止拷贝构造,取别名
ostream& operator <<(ostream& out,Stu& stu)
{
    //函数里面直接使用传进来的参数打印
    out << stu.name << endl;
    out << stu.sex << endl;
    out << stu.age << endl;
    out << "-------------------" << endl;
    return out;
}
//>> 输入重载
istream& operator >>(istream& in,Stu& stu)
{
    in >> stu.name >> stu.sex >> stu.age;
    return in;
}
void fun01()
{
    string str01 = "abc";
    cout << str01 << endl;

    Stu stu("张三","男",19);
    Stu stu02("李四","男",19);
    cout << stu << stu02 << endl;
}
void fun02()
{
    Stu stu;
    cin >> stu;
    cout << stu;
}

int main(int argc, char *argv[])
{
    fun01();
    fun02();
    return 0;
}

5.4 重载 + 运算符

效果:

使用+运算符将 自定义类型 对象的属性一一相加

分析:

+ 符号左边为 自定义类型,可以使用 全局函数重载 也可以使用 成员函数重载

示例:

#include 
#include 
#include 
using namespace std;
class Stu{
private:
    char name[50];
    char sex[10];
    int age;
public:
    Stu(){}
    Stu(char *name,char *sex,int age)
    {
        strcpy(this->name,name);
        strcpy(this->sex,sex);
        this->age = age;
    }
    Stu(const Stu& stu)
    {
        strcpy(this->name,stu.name);
        strcpy(this->sex,stu.sex);
        this->age = stu.age;
    }
    //调用该函数的对象就是符号左边的变量
    //参数符号右边的变量
    Stu& operator +(Stu& stu)
    {
        Stu *s = new Stu();
        //置0,新创建的是随机值,下面会乱码
        memset(s->name,0,50);
        strcat(s->name,this->name);
        strcat(s->name,stu.name);
        s->age = this->age + stu.age;
        //想返回的是Stu的引用个,而不是指针,所以是*s,
        //给s取值就是指针指向那片内存的地址,即Stu()这个对象
        return *s;
    }
};
void fun03()
{
    Stu s01("德玛","男",18);
    Stu s02("西亚","男",20);

    Stu s03 = s01 + s02; //德玛西亚
    cout << s03 << endl; //38
}
int main(int argc, char *argv[])
{
    fun03();
    return 0;
}

5.5 重载 == 运算符

效果:

比较类中成员变量值是否相同

分析:

符号左边为 自定义类型,可以使用全局函数重载也可以使用成员函数重载

示例:

#include 
#include 
#include 
using namespace std;
class Stu{
private:
    char name[50];
    char sex[10];
    int age;
public:
    Stu(){}
    Stu(char *name,char *sex,int age)
    {
        strcpy(this->name,name);
        strcpy(this->sex,sex);
        this->age = age;
    }
    Stu(const Stu& stu)
    {
        strcpy(this->name,stu.name);
        strcpy(this->sex,stu.sex);
        this->age = stu.age;
    }
    int operator ==(Stu& stu)
    {
        int x = strcmp(this->name,stu.name);
        int y = strcmp(this->sex,stu.sex);
        int z = this->age - stu.age;
        if(x == 0 && y == 0 && z == 0)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
};
void fun04()
{
    Stu s01("德玛","男",18);
    Stu s02("西亚","男",20);
    Stu s03("西亚","男",20);
    //s01 == s02
    //如果s01对象的属性值与s02的属性值,相同返回1,不同返回0
    cout << (s01 == s02) << endl; //0
    cout << (s03 == s02) << endl; //1
}
int main(int argc, char *argv[])
{
    fun04();
    return 0;
}

5.6 重载 ++ 运算符

注意:

++运算符分为:

  • ++在前,先自增在运算
  • ++在后,先运算在自增

所以需要重载两种

分析:

  • 当编译器看到 ++a(前置++),它就调用 operator++(Type& a)(全局函数),operator++()(成员函数)

  • 当编译器看到 a++(后置++),它就会去调用 operator++(Type& a,int)(全局函数),operator++(int)(成员函数)

示例:

#include 
#include 
#include 
using namespace std;
class Stu{
private:
    char name[50];
    char sex[10];
    int age;
public:
    Stu(){}
    Stu(char *name,char *sex,int age)
    {
        strcpy(this->name,name);
        strcpy(this->sex,sex);
        this->age = age;
    }
    Stu(const Stu& stu)
    {
        strcpy(this->name,stu.name);
        strcpy(this->sex,stu.sex);
        this->age = stu.age;
    }
    void operator ++()
    {
        //cout << "成员函数重载++前置" << endl;
        this->age++;
    }
    Stu& operator ++(int)
    {
        //cout << "成员函数重载++后置" << endl;
        Stu *old = new Stu(this->name,this->sex,this->age);
        this->age++;
        return *old;
    }
};
//全局函数
//void operator ++(Stu& stu)
//{
//    cout << "++前置调用" << endl;
//}
//void operator ++(Stu& stu,int)
//{
//    cout << "++后置调用" << endl;
//}
int main(int argc, char *argv[])
{
    Stu s01("德玛","男",18);
    ++s01;
    cout << s01;

    //先运算再自增,所以年龄还是原值
    Stu s02 = s01++;
    cout << s02 << endl;
    cout << s01 << endl;

    delete &s02;
    return 0;
}

结果:

04-C++ 类和对象-02_第1张图片

5.7 重载 ->与* 运算符

效果:

重载指针运算符, 实现智能指针

示例:

  • 代码推演:
#include 

using namespace std;

class A
{
private:
    int x;
public:
    A()
    {
        cout << "A的无参构造函数被调用了" << endl;
    }
    A(int x)
    {
        this->x = x;
        cout << "A的有参构造函数被调用了" << endl;
    }
    A(const A& a)
    {
        this->x = a.x;
        cout << "A的拷贝构造函数被调用了" << endl;
    }
    ~A()
    {
        cout << "A的析构函数被调用了" << endl;
    }
    void setX(int x)
    {
        this->x = x;
    }
    int getX()
    {
        return x;
    }
};
void fun01()
{
    A *a = new A(10);   //A的有参构造函数被调用了
}
int main(int argc, char *argv[])
{
    fun01();
    return 0;
}

观察以上代码,我们发现创建的对象没有被销毁,但是我们在编写代码时经常会忘记销毁,那该怎么办呢?

解决方案如下

#include 
using namespace std;
class A{
private:
    int x;
public:
    A()
    {
        cout << "A的无参构造函数被调用了" << endl;
    }
    A(int x)
    {
        this->x = x;
        cout << "A的有参构造函数被调用了" << endl;
    }
    A(const A& a)
    {
        this->x = a.x;
        cout << "A的拷贝构造函数被调用了" << endl;
    }
    ~A()
    {
        cout << "A的析构函数被调用了" << endl;
    }
    void setX(int x)
    {
        this->x = x;
    }
    int getX()
    {
        return x;
    }
};
//一个类作为一个类的属性,
//当外边这个类对象释放的时候,里面的类对象也会被释放
//所以可以在外边这个类的析构函数中,做释放的操作。
class FreeA{
private:
    //A类的对象作为属性,因为A的对象在创建时,使用的是new,指针接收
    //所以此处也用指针作为属性
    A* a;
public:
    //构造函数,包裹A的对象
    FreeA(A *a)
    {
        this->a = a;
    }
    ~FreeA()
    {
        //a为指针变量,需要释放,delete
        delete a;
    }
    //重载 ->
    A* operator ->()
    {
        return a;
    }
    //重载*
    A& operator *()
    {
        return *a;
    }
};
void fun01()
{
//    A *a = new A(10);
//    a->get_data();
    FreeA a1(new A(10));
    //要通过创建的对象获取或者修改 A的对象的值,即10,就得重载运算符 -> *
    // a1 -> get_data();就是要 a1->的结果 能得到 A的对象的指针,就可以调用get_data()
//    cout << a1 -> get_data() << endl;
    //或者 (*a1).get_data(); 指针a 取值(*a) 就是A的对象,直接.get_data();
    cout << (*a1).get_data() << endl;
}
int main(int argc, char *argv[])
{
    fun01();
    return 0;
}

结果:

04-C++ 类和对象-02_第2张图片

5.8 重载 () 运算符

作用:类 对象作为函数调用

返回值类型 operator()(参数列表)函数。

对象作为函数调用
    对象名(实参列表);
    一种仿函数

示例:

#include 

using namespace std;
//重载()运算符,可以使其对象作为函数进行调用
class A{
public:
    void operator()()
    {
        cout << "函数被调用" << endl;
    }
};
void fun01()
{
    A a;
    a();
	//隐式创建调用的就是()运算符
}

int main(int argc, char *argv[])
{
    fun01();
    return 0;
}
//函数被调用

5.9 重载 = 运算符

注意:

=重载时,可能会调用类本身的拷贝构造函数。

如果左值是没有创建的对象时,会调用拷贝构造函数.
如果左值是已创建的类对象,会执行 =重载函数,实现数据的拷贝。

示例:

#include 
using namespace std;
class B{
public:
    B()
    {
        cout << "构造函数" << endl;
    }
    B(const B& b)
    {
        cout << "拷贝构造" << endl;
    }
    void operator=(B& b)
    {
        cout << "重载的=号运算符" << endl;
    }
};
void fun02()
{
    B b1;
    //初始化时,调用拷贝构造
    B b2 = b1;
    //初始化后调用重载的=号运算符
    b2 = b1;
}
int main(int argc, char *argv[])
{
    fun02();
    return 0;
}
//构造函数
//拷贝构造
//重载的=号运算符

你可能感兴趣的:(C/C++,c++,开发语言)