欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

类和对象(中篇)

程序员文章站 2022-06-01 11:53:58
...
  • 类的6个默认成员函数
    构造函数、拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载

  • 构造函数
       特殊的成员函数,在创建对象时,由编译器自动来调用,并且在对象的生命周期内只调一次,来完成对象的构造以及初始化。
    特性:

   1.名字与类型相同
    2.没有返回值类型
    3.编译器来调(对象构造时,系统自动调用对应的构造函数)
    4.如果没有显示定义,编译器合成一个(没有参数)
    5.支持重载
    6.无参构造函数和带全缺省的构造函数都被称为----->缺省构造函数,缺省构造函数只能存在一个
class Date
{
public :
// 1.无参构造函数
Date ()
{}
// 2.带参构造函数
Date (int year, int month , int day )
{
_year = year ;
_month = month ;
_day = day ;
}
// 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 ;
};
  • 析构函数

        特殊的成员函数,当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个成员函数即析构函数。

特征:

1.析构函数名是在类名前加上字符~
2.无参数返回值
3.一个类有且只有一个析构函数,若未显示定义,系统会自动生成缺省的析构函数

注意:析构函数不是删除对象,而只是清理资源,即收回对象的空间y

  • 拷贝构造函数
        特殊的成员函数,创建对象时使用对象来进行初始化,这时所用的构造函数称为拷贝构造函数。

特性:

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

类和对象(中篇)

// 类类型对象的引用 , 一般情况下, const Date&
    // 特殊:
    Date(const Date& d)
    {
        this->_year = d._year;
        _month = d._month;
        _day = d._day;
    }

类和对象(中篇)

  • 深浅拷贝
    • 浅拷贝:如果一个类用于资源(堆,或者是其他系统的资源),当这个类发生复制时,资源也进行复制。
    • 深拷贝:一个类存在资源,复制时资源没有进行复制。
      类和对象(中篇)
class SeqList
{
public:
       SeqList()
       {
              cout << "SeqList():" << this << endl;
              _array = NULL;
              _size = 0;
              _capacity = 0;
       }
       SeqList(int n, DataType data)
       {
              cout << "SeqLiat(int,int,int):" << this << endl;
              _array = (DataType*)malloc(n * sizeof(DataType));
              for (int i = 0; i < n; i++)
              {
                     _array[i] = data;
              }
              _size = n;
              _capacity = n;
       }
       void PrintfSeqList()
       {
              for (int i = 0; i < _capacity; i++)
                     cout << _array[i] ;
              cout << endl;
       }
       ~SeqList()
       {
              cout << "~SeqList():" <<this->_array[1]<< this << endl;
              if (_array != NULL)
              {
                     free(_array);
                     _capacity = 0;
                     _size = 0;
              }

       }
private:
       DataType * _array;
       size_t _size;
       size_t _capacity;
};
int main()
{
       SeqList s1(10, 5);
       SeqList s2;
       s2 = s1;
       s1.PrintfSeqList();
       s2.PrintfSeqList();
       SeqList s3(10, 6);
       s3 = s1;
       return 0;
}

类和对象(中篇)

* 运算符重载

  • 在类里面定义,参数只有一个,因为它隐含着一个this指针
Data operator+(int day)
{
  Data temp(*this);
  temp._day += 1;
  return temp;
}
  • 在类外面定义参数要有两个,要把那个你要操作的对象加上
Data operator - (const Data& d,int day)
{
       Data temp(d);
       temp._day -= 1;
       return temp;
}

赋值运算符
类和对象(中篇)

所以,要自己写喽。当有了下面这个赋值运算符的函数时,就可以。先是d2 = d1,调用赋值运算符的重载,然后再用那个返回值给d3赋值,调用的是拷贝构造函数。

Data& operator=(const Data& d)
{
    if (this != &d)
     {
       _year = d._year;
       _month = d._month;
       _day = d._day;
      }
    return *this;
}
void Test ()
{
Date date1 ;
Date date2 = date1; // 调用拷贝构造函数
Date date3 ;
date3 = date1 ; // 调用赋值运算符的重载
}

要注意四点:

1.传参为什么传引用? —————- 不产生临时变量,提高效率
2.返回为什么是当前对象,而不是d ——– 和连续赋值的规则一样
3.返回值为什么不是void ———— 因为要能连续赋值
4.那个if语句是在判断什么 ———– 当自己给自己赋值时,就不用赋了,提高效率
4.为什么返回引用 ——- 提高效率

  • 前置++(返回的是类类型的引用)
Data& operator++()//前置++
{
       _day += 1;
       return *this;
}
  • 后置++(注意:返回的是类类型,因为我们创建了一个临时变量,不能返回栈空间的地址,还有operator的后面的参数必须是int型)
  Date operator++(int)
    {
        Date temp(*this);
        _day += 1;
        return temp;
    }

类和对象(中篇)

 //赋值
    Date& operator=(const Date& d)
    {
        if(this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }

        return *this;
    }

    //小于
    bool operator<(const Date& d)
    {
        if(_year < d._year ||
            _year == d._year && _month < d._month ||
            _year == d._year && d._month == d._month && _day < d._day)
        {
            return true;
        }

        return false;
    }

    //是否相等
    bool operator==(const Date& d)
    {
        return _year == d._year &&
            _month == d._month &&
            _day == d._day;
    }

    //是否不相等
    bool operator!=(const Date& d)
    {
        return !(*this == d);
    }

    // 前置++
    Date& operator++()
    {
        _day += 1;
        return *this;
    }

    // 后置++
    Date operator++(int)
    {
        Date temp(*this);
        _day += 1;
        return temp;
    }

    //取地址
    Date* operator&()
    {
        return this;
    }
  • 类的const成员函数

    在函数后面加const,const修饰this指针所指向的对象,也就是保证调用这个   const成员函数的对象在函数内不能被改变。
    

const修饰形参,一般和引用同时使用
const修饰返回值
const修饰类数据成员,必须在构造函数的初始化列表中初始化
const修饰类成员函数,实际修饰隐含的this,表示在类中不可以对类的任何成员进行修改

类和对象(中篇)
在const修饰的成员函数中要对类的某个数据成员进行修改,该数据 成员定义声明是必须加mutable关键字

  • 思考以下几种场景
    1. const对象可以调用非const成员函数和const成员函数吗?
      不可以调非const成员函数。
    2. 非const对象可以调用非const成员函数和const成员函数吗?
      都可以调。
    3. const成员函数内可以调用其它的const成员函数和非const成员函数吗?
      不可以调非const成员函数。
    4. 非const成员函数内可以调用其它的const成员函数和非const成员函数吗?
      都可以调。
class Date
{
public:
       Date(int year = 1900, int month = 1, int day = 1)
       {
              cout << "Date(int,int,int):" << this << endl;
              _year = year;
              _month = month;
              _day = day;
       }
       void SetYear(int year)
       {
              _year = year;
       }
       int GetYear()
       {
              return _year;
       }
       void PrintDate()const
       {
              cout << _year << "-" << _month << "-" << _day << endl;
       }
       void TestFunc1()
       {}
       void TestFunc2()const
       {}
       void TestFuncWithConst()const
       {
              TestFunc2();
              TestFunc1();//不可以
       }
       void TestFuncWithoutConst()
       {
              TestFunc1();
              TestFunc2();
       }
private:
       int _year;
       int _month;
       int _day;

};
int main()
{
       Date d1(2018, 7, 29);
       d1.PrintDate();
       d1.TestFunc1();
       d1.TestFunc2();
       const Date d2;
       d2.PrintDate();
       d2.TestFunc1();//不可以
       d2.TestFunc2();
}

类的取地址操作符重载 和 const修饰的取地址操作符重载
这两个默认成员函数一般不用重新定义,编译器默认会生成,
只有一种情况下,才会需要你自己重载这两个操作符,那就是你只想让别人获取你指定的内容。

Date* operator&()
{
       return this;
}
const Date* operator&()const
{
       return this;
}

若10个变量,只有两个需要改变,则在这个变量前面加上mutable。