C++ 类与对象(二)

第二章  类与对象


第二章  类与对象

前言

默认成员函数

构造函数

语法

explicit关键字

初始化列表

析构函数

拷贝构造函数

运算符重载

赋值重载

前置++ 和 后置++(哑元函数)的重载

友元函数 实现 (<< 及 >> )重载

普通对象的 &(取地址) 重载

const 关键字

const 对象的 &(取地址)重载

注意:只要成员函数中不需要修改成员变量都最好加上const。因为普通对象调不动const成员函数(常函数)

前言


        C++中任何类就算什么也不写(空类),编译器会自动生成6个默认成员函数

        默认成员函数:又称特殊成员函数,用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。即:我们不写一个,编译器就会自己生成一个;我们自己写了,编译器就不会生成。(隐含的意思:对于有些类,需要我们自己写;对于另外一些类,编译器默认生成就可以用)

默认成员函数


1.初始化和清理:           

        构造函数:主要完成初始化工作。               

        析构函数:主要完成清理工作。

2.拷贝赋值:

        拷贝构造:使用同类对象初始化创建对象。

        赋值重载:把对象赋值给另一个对象。

3.取地址重载:

        普通对象的取地址。

        const对象的取地址。 (这两个很少会自己去实现)

构造函数


语法

class 类名{
  类名(构造形参表){
  //主要负责初始化对象,即初始化成员变量
  }
};
  • 函数名与类名相同,没有返回类型
  • 构造函数在创建对象时自动调用和执行,不能向普通的成员函数通过对象去调用
  • 支持函数重载(参数个数不同包括类型不同或顺序不同

explicit关键字

class 目标类型{
  explicit 目标类型(源类型){...}
  };
  可以实现源类型到目标类型的隐式转换。
  注:使用explicit关键字,可以强制这种转换必须显式的完成。

初始化列表

        初始化列表,可以理解为对象的成员变量的定义的地方

        初始化时的顺序与声明的顺序的有关

注意:

每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

类中包含以下成员,必须放在初始化列表位置进行初始化:

       引用成员变量
       const成员变量
       自定义类型成员(且该类没有默认构造函数时)

class 类名{
  类名(形参表)
    :成员变量(初值)
    ,...
  {}
};

例子

class Date {
private:
    int _year;

    const int _n = 30;  // 给一个缺省值,如果构造函数给值了,使用构造函数的。
public:
    explicit Date(int year = 0)
        :_year(year)  // 初始化列表
        ,_n(20) // 初始化列表的体现 
    {
        //_n = 40;  因为是const不能被修改,报错
    }
};
int main() {
    Date d1;
    // 相当于构造出 tmp(2) 再用tmp拷贝构造d2(tmp)
    Date d2 = 2 ;  // 构造器加上 explicit  关键字, 就无法隐式转换 所以这句会报错
    
    // 拓展:如果有多个参数时,可以用大括号
    /*Date d3 = {2,3} ;*/
}

析构函数


        与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象里的资源的清理工作。

语法:

class 类名{
   // 构造函数
    // .....
    
  // 析构函数

  ~类名()
  {
    // 释放对象里开辟的空间
  }
};

注意:

        如果类中没有申请资源(比如开辟空间)时,析构函数可以不写,直接使用编译器生成的默认析构函数,如果有资源申请时,一定要写,否则会造成资源泄漏。

拷贝构造函数


1. 拷贝构造函数是构造函数的一个重载形式。

2. 拷贝构造函数的参数只有一个使用传值方式编译器直接报错,因为会引发无穷递归,推荐用引用做形参,指针也可以(不推荐)。

语法:

class Date {
private:
    int _year;
public:
   
    Date()
    :_year(2023)
    {}
    
    // 拷贝构造函数
    Date(const Date& d) // 不能 使用传值方式
    {
        _year = d._year; // 将 d 对象中数据复制给this(新创建的对象)
    }

};
int main() {
    Date d1;
    Date d2(d1); // 拷贝构造

}

注意事项:

        像上述的例子中没必要写拷贝构造函数。因为默认拷贝构造函数能完成简单内置类型的拷贝操作

        但是对于涉及空间开辟的情况。一定要写拷贝构造函数。(如果不写会造成浅拷贝,只拷贝了开辟空间的地址(造成两个对象指向同一个空间),对象销毁时,会析构两次(即释放两次一样的空间地址))

        建议加上const,防止被拷贝对象的数据被更改。

运算符重载


        operator 关键字,实现自定义类型的运算。

operator 函数中的操作数取决于参数个数

写在类中时,this 指针就算一个隐藏参数

operator 一般写在类中,方便通过 this 指针访问成员变量

operator也可以写在类外,此时会发生无法访问成员变量问题,可以这样解决:

将成员变量设为 public (不安全)

通过函数获取类中的成员变量值 (麻烦)

设置为友元函数(特殊情况推荐,比如重载  << 和 >>)

写在类中,最简单、省事,而且还可以使用 this 指针

赋值重载

    // d2 = d1  // 返回值是d4才能支持连等。
    Date& operator=(const Date& d) {
        if (this != &d) // 对自己给自己复制的判断
        {
            _year = d._year; // this -> d2 , d -> d1
        }
        return *this; // 支持连等
    }

赋值重载注意事项:

        与拷贝构造不同 d2对象已存在。

前置++ 和 后置++(哑元函数)的重载

        前置++:

    // ++d1
    Date& operator++() {
        // 实现++;并返回当前对象
    }

        后置++:

C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递

    // d1++
    Date operator++(int) { // 为了构成函数重载
        // 实现 先返回原来的值。在++
    }

前置-- 和 后置-- 是一样的。

友元函数 实现 (<< 及 >> )重载

#include <iostream>
using namespace std;

class Date {
private:
    int _year;
    int _month;
    int _day;
public:
    // 友元函数的声明
    friend ostream&  operator<<(ostream& out, const Date& d);  // 友元声明
    friend istream& operator>>(istream& input, Date& d);
    Date(int year = 0 , int month = 1, int day = 1)
        : _year(year)
        ,_month(month)
        ,_day(day) 
    {}

    // 这样不好
    // d << cout;只能这样用,
    // 默认左参数是this 即 d -> this
    /*void operator<<(ostream& out) {
        out << _year << "-" << _month << "-" << _day << endl;
    }*/
};

// cout -> ostream
// cin -> istream

//  使用这个返回值 就可以使用 cout << d << d2;连续输出
ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "/" << d._month << "/" << d._day << endl; // 友元使用后就可以直接访问对象里private里的内容
    return out; 
}


istream& operator>>(istream& input, Date& d) {
    input >> d._year >> d._month >> d._day;
    return input;
}

int main() {
    Date d;
    
    /*d << cout;*/
    /*cout << d;
    int i = 0;
    cout << i;*/

    cin >> d;
    cout << d;
    return 0;
}

普通对象的 &(取地址) 重载

        

// 没必要写,除非不想被取地址。编译器会自动生成
    Date* operator&() {
        cout << "operator&()\n";
        return this;
    }

const 关键字


const 修饰可以提高程序的健壮性

const 常被用来修饰引用、指针

被const修饰后,就不能再修改了(权限缩小)

const 对象的 &(取地址)重载

    const Date* operator&() const  // 相当于隐含的参数变为 const 类名* this 
    {  
        cout << "operator&() const\n";
        return this;
    }

注意:只要成员函数中不需要修改成员变量都最好加上const因为const对象调不动普通成员函数

const对象只能调用const成员函数(常函数)

class Date {
private:
    int _year;
    int _month;
    int _day;
public:

    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;

    }
    // 打印
    void print() const  // -> void print(Date* this)
     // 这里加上const  f函数中才能调用
    {
        cout << _year << "-" << _month << "-" << _day << endl;
        //this->_year = 10;  不能修改了,const修饰保护了*this
    }
};
void f(const Date& d) {
    d.print(); //  -> d.print(&d)
    // 因为传的是const Date& d 
}
int main() {
    Date d(2020, 1, 20);
    f(d);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值