C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数

时间:2022-09-09 16:54:12

在C++中,有6个默认的成员函数(即如果不写成员函数,系统就会自动调用)。

C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数
一,构造函数

构造函数是特殊的成员函数。作用是: 在创建对象时,对对象进行初始化
其特征有:
①构造函数是成员函数,可以写在类体外,也可以写在类体内。
②函数名与 类名相同
③不指定类型说明, 无返回值
④实例化对象时系统自动调用。
⑤构造函数可以 重载
⑥如果在定义类时,没有写构造函数,系统就会自动生成一个缺省的构造函数,但是如果自己写了构造函数,系统调用写的那个函数。
⑦无参的构造函数和全缺省的构造函数都是缺省的构造函数。
下面我们通过一个具体的例子来认识构造函数:
1.无参的构造函数
#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date()
	{
		cout<<"Date"<<endl;
	}
void Print() 
	{ 
		cout<<_year<<"-"<<_month<<"-"<<_day<<endl; 
	}
private: 
	int _year; 
	int _month; 
	int _day;
};
int main()
{
	Date d;
	d.Print();    
	system("pause"); 
	return 0;
}

 
C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数

无参的构造函数,在创建对象初始化时,什么都不会做。此时,如果不写构造函数,也会调用系统自动生成的构造函数,同样什么也不会做。但是如果类中有自定义的类,那么用自己写的构造函数就会初始化。

2.有参的构造函数
#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
		cout<<"Date"<<endl;
	}	 
	void Print() 
	{
		cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
	}
private: 
	int _year; 
	int _month; 
	int _day;
};
int main()
{ 
	Date d; 
	d.Print(); 
	Date d1(2018,3,25); 
	d1.Print(); 
	system("pause"); 
	return 0;
}

 
C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数

有参的构造函数,创建对象调用构造函数初始化时的值就是传的值。

3.缺省的构造函数
⑤全缺省的构造函数
#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		cout<<"Date"<<endl;
	}
    void Print()
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
private:
    int _year; 
    int _month;
    int _day;
};
int main()
{
	Date d;
	d.Print();
	Date d1(2018,3,25);
	d1.Print();
	system("pause");
    return 0;
}

全缺省的构造函数,如果在创建对象时不传任何参数,那么在调用构造函数时就会调用构造函数进行初始化,并不会生成随机值。而当我们传了参数时,就会调用我们自己传的参数。所以,一般建议在写构造函数时采用全缺省的构造函数。当类中有自定义的类时,调用缺省的构造函数并不是什么都不做。
②半缺省的构造函数
#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900,int month = 1)
	{
		_year = year;
		_month = month;
		_day = 1;
		cout<<"Date"<<endl;
	}
    void Print()
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
private:
    int _year; 
    int _month;
    int _day;
};
int main()
{
	Date d;
	d.Print();
	Date d1(2018,3);
	d1.Print();
	system("pause");
    return 0;
}

半缺省的构造函数,缺省参数怎么缺省(从右向左进行缺省),我在前面的博客中有介绍。。。。。。
深入探索构造函数:

        在C++中,使用初始化列表也可以初始化对象,而且比构造函数更高效。(因为初始化列表无论写不写,系统都会调用)

用法:以冒号开始,以逗号分隔,括号里面是要初始化的值。 C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数
有些变量必须在初始化列表中进行初始化:
①自定义类型的类如果没有缺省的构造函数,必须在初始化列表初始化。
②引用类型的成员变量。
③常量。
注意:初始化列表按声明的顺序进行初始化。

二,析构函数

作用:析构函数的作用刚好和构造函数的功能相反,它的作用是:在一个对象的生命周期结束时,用析构函数进行清理一些东西。析构函数的特点与构造函数很相似。

#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		cout<<"Date"<<endl;
	}
	~Date()
	{
		cout<<"~Date"<<endl;
	}
    void Print()
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
private:
    int _year; 
    int _month;
    int _day;
};
int main()
{
	Date d;
	d.Print();
	system("pause");
        return 0;
}

在VS2010中运行结果中并不会看到析构函数被调用(可能是被优化了),上面的截图是在Linux中运行的结果。我们可以看到,在函数调用完成后,就会调用析构函数进行清理工作(清理一些在堆上开辟的空间,想这样普通的函数在函数执行完后就会自动释放内存,并不需要去清理)。

三,拷贝构造函数

定义:创建对象时用同类对象来进行初始化,用一个已知的对象来创造一个新的对象。

格式:

<类名>::<拷贝构造函数名>(<类名>&<引用名>)

{
    <函数体>
}

特点:

①拷贝构造其实是构造函数的重载;
②拷贝构造函数在传参时必须使用引用,如果使用传值的方式,就会引发无穷递归。
③如果没有写拷贝构造函数,系统会自动生成一个缺省的拷贝构造函数。
#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900,int month = 1,int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		cout<<"Date"<<endl;
	}
	~Date()
	{
		cout<<"~Date"<<endl;
	}
	Date(Date& d)
	{
		_year =d._year;
		_month =d._month;
		_day = d._day;
		cout<<"Date&"<<endl;
	}
    void Print()
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
private:
    int _year; 
    int _month;
    int _day;
};
int main()
{
	Date d(2018,3,25);
	d.Print();
	Date d1(d);
	d1.Print();
	system("pause");
    return 0;
}
当我们不写拷贝构造函数时,系统也会自动生成一个拷贝构造。而且在上面的这个事例中,系统自动生成的拷贝构造函数也可以完成同样的拷贝。缺省的拷贝构造函数,会依次拷贝成员进行初始化。 C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数
#include<iostream>
#include<windows.h>
using namespace std;
class SeqList
{
public:
	SeqList(const size_t capacity = 0)
	{
		if(capacity > 0)
		{
			data = (int*)malloc(sizeof(int)*capacity);
			_size = 0;
			_capacity = capacity;
		}
		else
		{
			data = NULL;
			_size = 0;
			_capacity = capacity;
		}
	}
	~SeqList()
	{
		if(data)
		free(data);
		_size = 0;
		_capacity = 0;
	}
private:
	int* data;
	size_t _size;
	size_t _capacity;
};
int main()
{
	SeqList L(100);
	SeqList L1(L);
	system("pause");
	return 0;
}

就像这个顺序表的拷贝构造,就会存在问题。 C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数
而且,当对象的生命周期结束时,就会调用它的析构函数进行清理,但是我们使用了拷贝构造创建了L1对象, 而且两个指向同一块空间,当一块空间被清理2次时,程序就会崩溃。 C++的6大成员函数,构造函数(初始化列表),析构函数,拷贝构造函数,运算符重载,const成员函数

四,运算符重载

作用:提高代码的可读性。

特点:函数名为:operator+合法的运算符。
C++不能重载的5个运算符:.*/::/sizeof/?:/.
注意:运算符重载也不可以改变运算符的优先性。。。在后面的日期类会有详细的例子。

五,const成员函数

用const修饰的对象,必须用const修饰的函数实例化。而非const修饰的函数,有const和非const实例化的函数都可以用。因为权限只能缩小,不能放大。

#include <iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900,int month = 1,int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
    void Print()
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
    void Print() const    //void Print(const Date* this)
    {
        cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
    }
private:
    int _year; 
    int _month;
    int _day;
};
int main()
{
	Date d(2018,3,25);
	d.Print();
	const Date d1(2018,4,3);
	d1.Print();
	system("pause");
    return 0;
}

上面用const修饰的对象,必须用const修饰的函数去实现。
日期类的代码就充分利用了这几个成员函数,下面是我实现的日期类的函数:
 
#pragma once

class Date
{
public:
    Date(int year = 1900 ,int month = 1 ,int day = 1 )
        :_year (year)
        ,_month (month)
        ,_day (day)
    {}
    bool IsInvalid();
    int GetCorrectDay(int _year,int _month);
    bool IsLeapYear(int _year)
    {
        return ((_year % 4 == 0 && _year % 100 != 0) || _year % 400 == 0);
    }
    void Print();
    Date& operator=(const Date& d);
    bool operator<(const Date& d);
    bool operator==(const Date& d);
    bool operator!=(const Date& d);
    bool operator<=(const Date& d);
    bool operator>(const Date& d);
    bool operator>=(const Date& d);
    Date operator++(int);   //后置++
    Date& operator++();     //前置++(默认是)
    Date operator--(int);
    Date& operator--();
    Date operator+(const int day);
    Date& operator+=(const int day);
    Date operator-(const int day);
    Date& operator-=(const int day);
    int operator-(Date& d);
private:
    int _year;
    int _month;
    int _day;
};

#include <iostream>
using namespace std;
#include"Date.h"
bool Date::IsInvalid()
{
    if(this->_year < 0 || this->_month < 0 || this->_month > 13 || this->_day < 0 || this ->_day > GetCorrectDay(this->_year,this->_month))
    {
        return false;
    }
    return true;
}
int Date::GetCorrectDay(int year,int month)
{
    int m_day = 0;
    int day[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};
    if(month == 2 && IsLeapYear(year))
    {
        m_day = 29;
    }
    else
    {
        m_day = day[month];
    }
    return m_day;
}
void Date::Print()
{
    cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
Date& Date::operator=(const Date& d)
{
    _year = d._year ;
    _month = d._month ;
    _day = d._day ;
    return *this;
}
bool Date::operator<(const Date& d)
{
    if(_year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day ))
    {
        return true;
    }
    return false;
}
bool Date::operator==(const Date& d)
{
    if(_year == d._year && _month == d._month && _day == d._day)
    {
        return true;
    }
    return false;
}
bool Date::operator!=(const Date& d)
{
    if((*this == d) == false)
    {
        return true;
    }
    return false;
}
bool Date::operator<=(const Date& d)
{
    if(*this < d || *this == d)
    {
        return true;
    }
    return false;
}
bool Date::operator>(const Date& d)
{
    if(!(*this < d) && !(*this == d))
    {
        return true;
    }
    return false;
}
bool Date::operator>=(const Date& d)
{
    if((*this > d) || (*this == d))
    {
        return true;
    }
    return false;
}
Date Date::operator+(const int day)
{
    if(day < 0)
    {
        return (*this - (-day));
    }
    Date tmp(*this);
    tmp._day  += day;
    while(tmp.IsInvalid() == false)
    {
        if(tmp._month > 12)
        {
           tmp. _year += 1;
           tmp._month = 1;
        }
        int m_day = GetCorrectDay(tmp._year,tmp._month);
        tmp._day -= m_day;
        ++tmp._month;
    }
    return tmp;
}
Date& Date::operator+=(const int day)
{
    *this = *this + day;
    return *this;
}
Date Date::operator-(const int day)
{
    if(day < 0)
    {
        return (*this + (-day));
    }
    Date tmp(*this);
    tmp._day -= day;
    while(tmp.IsInvalid() == false )
    {
        if(tmp._month <= 1)
        {
            tmp._month = 12;
            tmp._year -= 1;
        }
        int m_day = GetCorrectDay(tmp._year,tmp._month - 1);
        tmp._day -= m_day;
        --tmp._month;
    }
    return tmp;
}
Date& Date::operator-=(const int day)
{
    *this = *this - day;
    return *this;
}
Date Date::operator++(int)
{
    Date tmp(*this);
    *this += 1;
    return tmp;
}
Date& Date::operator++()
{
    *this += 1;
    return *this;
}
Date Date::operator--(int)
{
    Date tmp(*this);
    *this -= 1;
    return tmp;
}
Date& Date::operator--()
{
    *this -= 1;
    return *this;
}
int Date::operator-(Date& d)
{
    int count = 0;
    while(*this != d)
    {
        if(*this > d)
        {
            ++d;
            ++count;
        }
        else if(*this < d)
        {
            ++(*this);
            ++count;
        }
    }
    return count;
}
int main()
{
    Date d(2018,3,27);
    Date d1(2018,3,3);
    cout<<(d == d1)<<endl;
    cout<<(d < d1)<<endl;
    cout<<(d <= d1)<<endl;
    cout<<(d > d1)<<endl;
    cout<<(d >= d1)<<endl;
    cout<<(d1 - d)<<endl;
    d = d + 430;
    d.Print();
    --d;
    d.Print();
    d++;
    d.Print();
    d--;
    d.Print();
    d = d + 2;
    d1 = d1 + 30;
    d.Print();
    d1.Print();
    return 0;
}

日期类的代码都是在Linux中实现的。。。。。。。。。。。。