C++类和对象——四个默认成员函数+运算符重载

本文详细介绍了C++中的类与对象概念、访问限定符、封装性、对象大小计算等内容,并深入探讨了构造函数、拷贝构造函数、析构函数及运算符重载等高级特性。

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

1:整理类和对象的基础知识,如类的定义,访问限定符,面向对象封装性,对象的大小计算等等。
1)定义
类就是定义了一个新的类型和一个新的作用域。对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。
每个类可以有成员,也可没有,成员可以是数据、函数或类型别名。
2)访问限定符
访问限定符分为:public(公有)、private(私有)和protected(保护)
1>public成员可以从类外部直接访问,private/protected成员不能从类外部直接访问。
2>每个限定符在类体中可使用多次,它的作用域是从该限定符出现开始到下一个限定符之前或类体结束前。
3>类体中如果没有定义限定符,则默认为私有的。
4>类的访问限定符体现了面向对象的封装性。
3)面向对象封装性
作用域分为:全局域、局部域、类域和名字空间域
类的作用域:
1>每个类都定义了自己的作用域,类的成员(成员函数/成员变量)都在类的这个作用域内,成员函数内可任意访问成员变量和其它成员函数。
2>对象可以通过 . 直接访问共有成员,只想对象的指针可以通过->直接访问对象的共有成员。
3>在类外定义成员,需要使用::作用域解析符指明成员属于哪个类域。
这里写图片描述
4)类实例化对象
1>类只是一个模型一样的东西,限定了类有那些成员,定义一个类并没有分配实际的内存空间来存储它。
2>一个类可以实例化多个对象,实例化出的对象占实际的物理空间存储类成员变量。
比如:类实例化出对象,就像使用设计图纸建造房子,类就像设计图纸,对象就像房子。同样类也是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
这里写图片描述
5)对象的大小计算
每个对象的大小是类中所有成员变量的大小之和,当然这里也遵循内存对齐原则。
这里写图片描述
只有成员变量占空间,成员函数在公共代码区,不占空间。也遵循内存对齐原则。
1>为什么要内存对齐?
效率问题,取数据时取字节好取。以空间换时间。
2>空类(没有成员)大小是?为什么?
空类分配一个字节,用于占位,表示该类的存在。
结构体内存对齐规则:
1>第一个成员在结构体变量偏移量为0的地址处。
2>其他成员变量要对齐到某个数字(对齐数)的整数倍的地址除。
//对齐数=编译器默认的一个对齐数与该成员中的较小值。
vs默认值为8
linux中为4
3>结构体的总大小为最大对齐数(每个成员变量除了成员变量都有一个对齐数)的整数倍数。
4>如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍数处,结构体的整数大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
参考:结构体内存对齐
例:计算A、B类的大小

class A
{
    char ch;
    double d;
};
class B
{
     char ch1;
     A a;
     char ch2;
};

VS下,A占16个字节,B占32个字节。
这里写图片描述
2:整理四个默认成员函数及运算符重载相关知识

1)构造函数
成员变量为私有时,要对其初始化,必须用一个公有成员函数来进行。同时这个函数应该有且仅在自定义对象时自动执行一次,这时调用的函数称为构造函数(constructor)。
构造函数是特殊的成员函数,其特征如下:
1>函数名与类名相同;
2>无返回值。
3>对象实例化时,系统自动调用对应的构造函数。
4>构造函数可以重载。
5>构造函数可以在类中定义,也可以在类外定义。
6>如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。
7>无参的构造函数和全缺省的构造函数都认为是缺省构造函数,并缺省的构造函数只能有一个。
无参构造函数和带参的构造函数:

class Date
{
public:
     //1.无参构造函数
     Date()
     {}
     //2.带参构造函数
     Date(int year,int month,int day)
     {
          _year=year;
          _month=month;
          _day=day;
     }
     private:
        int _year;
        int _month;
        int _day;
} ;
void TestDate1()
{
     Date d1;//调用了无参构造函数
     Date d2(2017,10,17);//调用带参构造函数
     Date d3();//没调构造函数,无参构造函数调用时不带括号,故这里只是定义了d3
}

带缺省参数的构造函数:

class Date
{
   public:
   //3.缺省参数的构造函数
    date(int year=2000;int month=1;int day=1)
    {
        _year=year;
        _month=month;
        _day=day;
     }        
   //4.半缺省参数的构造函数
    date(int year;int month=1;)
    {
        _year=year;
        _month=month;
        _day=1;
    }   
    private:
       int _year;
       int _month;
       int _day; 
};
void Test()
{
    Date d1;//调用缺省构造函数
    Date d2(20171013);//调用缺省构造函数
}

注:若缺省参数声明和定义分离,则可在声明或定义中给默认参数。
2)拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数,拷贝构造函数是特殊的构造函数。
特征:
1>拷贝构造函数其实是构造函数的重载。
2>拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
3>若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员进行初始化。

class Date
{
    public:
        Date()
        {}
        //拷贝构造函数
        date(const Date& d)
        {
            _year=d._year;
            _month=d._month;
            _day=d._day;
        }
   private:
        int _year;
        int _month;
        int _day;
};
void TestDate1()
{
    Date d1;
    //下面两种方法都是调用拷贝构造函数,是等价的
    Date d2(d1);//调用拷贝构造函数
    Date d3=d1;//调用拷贝构造函数
  }

这里写图片描述
3)析构函数
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor)
析构函数是特殊的成员函数,特点:
1>析构函数在类名加上字符~。
2>析构函数无参数无返回值。
3>一个对象有且只有一个析构函数,不能重载。若未显示定义,系统会自动生成缺省的析构函数。
4>对象生命周期结束时,C++编译系统会自动调用析构函数。
5>注意析构函数体内并不是删除对象,而是做一些清理工作。

class Date
{
public:
     //析构函数
     ~Date()
     {}
private:
     int _year;
     int _month;
     int _day;
};
//例(解释5>)
class Array
{
public:
    Array(int size)
    {
        _ptr=(int *)malloc(size*sizeof(int));
    }
    //这里的析构函数需要完成清理工作(释放空间)
    ~Array()
    {
         if(_ptr)
         {
             free(_ptr);
             _ptr=0;
         }
    }
 private:
      int* _ptr;
 }        

注:动态分配的对象只有在指向该对象的指针被删除时才撤销。如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就会一直存在,从而导致内存泄漏,而且,对象内部使用的任何资源也不会释放。
4)赋值操作符重载
拷贝构造函数是创建对象时,使用一个已有对象来初始化这个准备创建的对象。
赋值运算符的重载是对一个已存在的对象进行拷贝赋值。

class Date
{
   public:
       Date()
       {}
       //拷贝构造函数
       Dateconst Date& d)
       :_year(d._year)
       ,_month(d._month)
       ,_day(d.day)
       {}
       //赋值操作符的重载
       //1.思考为什么operator=赋值函数需要一个Date&的返回值,使用void做返回值可不可以?
       Date& operator = (const Date& d)
       {
          //2.这里的if条件判断是在检查什么?
          ifthis!=&d)
          {
             this->_year=d._year;
             this->_month=d._month;
             this->_day=d._day;
          }
          return *this;
       }
    private:
        int _year;
        int _month;
        int _day;
};
void Test()
{
     Date d1;
     Date d2=d1;//调用拷贝构造函数
     Date d3;
     d3=d1;//调用赋值运算符的重载
 }

回答:
问题1:因为返回的结果要用于对新建对象的初始化,如果返回void,则不能完成初始化;
问题2: if是在判断等号左右两边的对象是不是同一个。
5)运算符重载
为了增强程序的可读性,C++支持运算符重载。
运算符重载特征:
1>operator+合法的运算符 构成函数名(重载<运算符的函数名:operator<)。
2>重载运算符以后,不能改变运算符的优先级、结合性、操作数个数。
注:5个C++不能重载的运算符:.* :: sizeof ? : .
3:整理隐含的this指针等等,及对运算符重载背后做的事情。
1>每个成员函数都有一个指针形参,它的名字是固定的,称为this指针,this指针是隐式的。(构造函数比较特殊,没有这个隐含this形参)
2>编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参即this指针。
3>this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。
这里写图片描述
重载运算符的调用和编译器处理:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值