[C++]__类的默认函数__

本文详细介绍了C++中默认成员函数的概念,包括构造函数、拷贝构造函数、析构函数、赋值运算符重载等。此外,还讨论了this指针的使用、运算符重载、内联函数、友元函数以及类的静态成员等主题,旨在深入理解C++类的成员函数及其特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述>
>

默认的成员函数

  • 隐含的this指针

    
    #include<iostream>
    
    using namespace std;
    
    class Date{
    private:
        int _year;
        int _month;
        int _day;
    public:
    void ShowDate() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
    };
    
    int main() {
    Date d1;
    d1.ShowDate();
    return 0;
    }
    • 在void ShowDate()的时候,其实是创建的void ShowDate(Date * this)。

    1. 每个成员函数都有一个指针形参,它的名字是固定的,称为this指针,this指针是隐式的。但是构造函数没有这个隐含的this指针。
    2. 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
    3. 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
    4. this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。但是我们可以在成员函数中使用this指针,比如this->_name;,this是可以写也可以不写的。同时this也可以作为返回值。

默认的成员函数

这里写图片描述

  • ①构造函数(初始化)

    
    #include<iostream>
    
    using namespace std;
    
    class Date{
    private:
        int _year;
        int _month;
        int _day;
    public:
    void ShowDate() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    Date() {
        cout << "无参数" << endl;
    }
    Date(int year, int month, int day){
        cout << "带参数" << endl;
          _year = year;
          _month = month;
          _day = day;
    }
    
    };
    
    int main() {
    Date d1;
    d1.ShowDate();
    Date d2(2018, 2, 2);
    d2.ShowDate();
    Date d3();
    return 0;
    }

    以上代码的输出结果为:

    调用构造函数时前面是 对象名 + .

    在构造函数无参时,不可以调用Date d1();即无参时调用不加括号。

    成员变量如果是内置类型,构造函数什么都不做,而如果包含自定义类型,例如:Time t1;,则构造函数会调用它的构造函数,对其进行初始化。

  • 缺省的构造函数:即:无参数或者全缺省。(建议写全缺省的)

    
    #include<iostream>
    
    using namespace std;
    
    class Date {
    private:
    int _year;
    int _month;
    int _day;
    public:
    void ShowDate() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    Date(int year=2018,int month=1,int day=1) {
        cout << "全缺省" << endl;
        _year = year;
        _month = month;
        _day = day;
    }
    };
    
    int main() {
    Date d1;
    d1.ShowDate();
    Date d2(2018,2,2);
    d2.ShowDate();
    system("pause");
    return 0;
    }
    
    #include<iostream>
    
    using namespace std;
    
    class Date {
    private:
    int _year;
    int _month;
    int _day;
    public:
    void ShowDate() {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    Date(int year, int month=1) {
        cout << "半缺省" << endl;
        _year = year;
        _month = month;
        _day = 1;
    }
    };
    
    int main() {
    Date d1(2018,2);
    d1.ShowDate();
    Date d2(2018);
    d2.ShowDate();
    system("pause");
    return 0;
    }

    输出结果为:

  • ②拷贝构造函数

    创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函 数。
    特征:

    1. 拷贝构造函数其实是一个构造函数的重载。
    2. 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
    3. 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
  • 定义拷贝构造函数:

    Date(const Date& d){//加const是防止被拷贝的对象被改变,不加引用会变为无穷递归
      _year = d._year;
      _month = d._month;
      _day = d._day;
    }
    //调用时
    Date d1;
    Date d2(d1);
    Date d3=d1;

    如果我们将成员变量的访问属性设为private,在这里也是可以直接访问私有的成员变量,因为:

    • 在类的成员函数中可以直接访问同类对象的私有/保护成员
    • C++的访问限定符是以类为单位的,也就是说在这个单位内的成员可以互相访问。
  • 不写拷贝构造函数会自动生成,也会起作用。

  • ③析构函数(清理工作)

当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor)。
构造函数是特殊的成员函数,其特征如下:

  1. 析构函数在类名加上字符 ~
  2. 析构函数无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  5. 注意析构函数体内并不是删除对象,而是做一些清理工作。

    • 首先要判断是否需要清理,析构函数是自动调用的,如果成员变量有自定义类型,会调用该类型的析构函数。
    • 运算符重载

运算符重载特征:

  1. operator+ 合法的运算符 构成函数名(重载<运算符的函数名:operator< )。
  2. 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。

    • 五个不能被重载的运算符:.*::sizeof?:.
    • ④赋值运算符重载

拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。

赋值运算符的重载是对一个已存在的对象进行拷贝赋值。

Date d1 ; Date d2 = d1; // 调用拷贝构造函数
Date d3 ; d3 = d1 ; // 调用赋值运算符的重载

日期类的赋值运算符重载:https://blog.youkuaiyun.com/Acoustic323/article/details/79717204

Date& operator=(const Date& d){
    if(this!&d){
        this->_year=d._year;
        this->_month=d._month;
        this->_day=d._day;
    }
    return *this;//*this会生成一个临时变量且会调用拷贝构造函数。
}
//可以用引用返回,也可以无返回值,void,但是会影响连续赋值

这里写图片描述

  • 类的成员变量有两种初始化方式:

    1. 初始化列表。

    2. 构造函数体内进行赋值。

      初始化列表以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号中进行初始化。尽量使用初始化列表进行初始 化,因为它更高效。不管你写不写初始化列表,都会走一遍初始化列表。

      • 必须放在初始化列表里面的成员变量
    3. 常量成员变量。(常量创建时必须初始化)

    4. 引用类型成员变量。(引用创建时必须初始化)
    5. 没有缺省构造函数的类成员变量。

    成员变量按声明顺序依次初始化,而非初始化列表出现的顺序。

  • ⑤取地址操作符重载

Date* operator&(){
    return this;
}
  • ⑥const修饰的取地址操作符重载
const Date* operator&() const{
    return this;
}

如果需要一个功能为:不能取地址,有两种实现方法:

  1. 将return this改为return NULL;
  2. 只声明取地址符的重载,不定义。(会有报错,直接警告你不可以取地址)

    • 思考:
  3. const对象可以调用非const成员函数和const成员函数吗?

    • 不可以
  4. 非const对象可以调用非const成员函数和const成员函数吗?
    • 可以
  5. const成员函数内可以调用其它的const成员函数非const成员函数吗?
    • 不可以
  6. 非const成员函数内可以调用其它的const成员函数非const成员函数吗?

    • 可以

    • 宏的优缺点:

    • 优点:
    • 宏常量——增强可维护性;
    • 宏函数——增强效率,因为没有压栈的过程。
    • 缺点:
    • 不方便调试/函数可以调试形成栈帧。
    • 没有类型安全的检查;
    • 可读性比较差,宏函数的可维护性比较差,容易出错。
#define Swap(a,b) {int temp=a;\  //宏要写在一行 可以使用换行符\
                        a=b;\
                        b=temp;}

为了防止多次调用Swap时会多次生成temp变量可以在调用时加花括号。

  • 内联函数(inline)

    可以调试,会形成栈帧,在releuse下会展开,debug不会展开。

    1. inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的的函数不适宜使用内联。
    2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉 内联。
    3. inline必须函数定义放在一起,才能成为内联函数,仅将inline放在声明前是不起不作用的。
    4. 定义在类内的成员函数默认定义为内联函数。
    class Date { 
    public :     
      void Func () // 定义在类内部默认为内联函数    
      {}
      void Display();
    private :     
      int _year ;     // 年     
      int _month ;    // 月     
      int _day ;      // 日 };
    inline void Date::Display (){  // 成员函数定义为内联     
      cout<<"year:" <<_year<< endl;     
      cout<<"month:" <<_month<< endl;     
      cout<<"day:" <<_day<< endl; 
    }
    inline void Test()              // 全局函数定义为内联 {}

  • c++建议以 const/枚举/内联来替代宏

  • 友元函数(friend)

    在类外的函数想调用私有的成员变量,可以在类中定义成“友元函数”。

    在C++中友元函数允许在类外访问该类中的任何成员,就象成员函数一样,友元函数用关键字friend说明。

    1. 友元函数不是类的成员函数。
    2. 友元函数可以通过对象访问所有成员,私有和保护成员也一样。
    class A{
      friend void Fun();//友元函数
      friend class B;//友元类
    }
    void Fun(){
    
    }
    class B{
    
    }

    友元是单向的,若想相互访问,在两个类内部都要定义友元。

    尽量少使用友元,会破坏封装。

    但是为什么存在友元?例如<<的重载

    • 输入输出运算符的重载的友元函数。
    class Date { 
    public :// 1:operator<<可以定义为成员函数吗?     // 2:返回值定义为void可以吗?为什么? 
      friend ostream & operator<< ( ostream& os , const Date& d );     
      friend istream & operator>> ( istream& is , Date& d);
    private :     
      int _year ;     // 年     
      int _month ;    // 月     
      int _day ;      // 日 };
    ostream & operator<<( ostream& os , const Date& d) {     
      os<<"year:" <<d. _year<<endl ;     
      os<<"month:" <<d. _month<<endl ;     
      os<<"day:" <<d. _day<<endl <<endl;
       return os ; 
    }
    // cin.operator<<(cin, d1) 
    istream & operator>> ( istream& is , Date& d) {
      cout<<" 请分别输入年月日: "<<endl ;     
      is>>d ._year;     
      is>>d ._month;     
      is>>d ._day;
      return is ;
    }
    void Test () {
      Date d1 ;     
      cin>>d1 ;     
      cout<<d1 ; 
    }
    
    
  • 类的静态成员

    1. 类里面static修饰的成员,成为静态类成员。
    2. 类的静态成员是该类型的所有对象对象所共享。
      这里写图片描述
      可以统计总共创建了多少次对象:
class Date { 
public :    
    Date ()    {          
        cout<<"Date ()" <<endl;         
        ++ sCount;    }
     void Display ()    {          
         cout<<"year:" <<_year<< endl;          
         cout<<"month:" <<_month<< endl;          
         cout<<"day:" <<_day<< endl;    }
     // 静态成员函数     
    static void PrintCount()    {          
        cout<<"Date count:" <<sCount<< endl;    
    } 
private :     
    int _year ;     // 年     
    int _month ;    // 月     
    int _day ;      // 日
private :     
    static int sCount; // 静态成员变量,统计创建时间个数 
};
// 定义并初始化静态成员变量 
int Date::sCount = 0;
void Test () {     
    Date d1 ,d2;
     // 访问静态成员     
    Date::PrintCount (); 
}

没有this指针,不能调用Show,不能访问对象。

静态成员属于类,也属于类中所有的对象。

  • Date d1;
  • Date ();//临时对象,匿名对象,生命周期只在当前一行,用完后马上析构不占用空间
  • fun(Date());
  • N中构造拷贝构造的优化
    Test1中调用了2次AA的拷贝构造函数,1次AA的赋值运算符函数的重载。
    Test2中调用了2次AA的拷贝构造函数,0次AA的赋值运算符函数的重载。
    Test3中调用了3次AA的拷贝构造函数,0次AA的赋值运算符函数的重载。
class AA{
};
AA f(AA a)
{
    return a;
}
void Test1()
{
    AA a1;
    a1 = f(a1);
}
void Test2()
{
    AA a1;
    AA a2 = f(a1);
}

void Test3()
{
    AA a1;
    AA a2 = f(f(a1));
}

Test1
Test2
Test3
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值