详解C++类中的6个默认成员函数
2017-08-08 12:37
387 查看
构造函数
定义
成员变量为私有的,要对他们进行初始化,必须用一个共有成员函数来进行。同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数(constructor)。特征:
函数名与类名相同。无返回值。
对象构造(对象实例化)时系统自动调用对应的构造函数。
构造函数可以重载。
构造函数可以在类中定义,也可以在类外面定义。
如果类定义中没有给出构造函数,则C++编译器自动生成一个缺省的构造函数,但只要定义了一个构造函数,系统就不会自动生成缺省的构造函数。
无参的构造函数和全缺省的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个。
代码举例
class Date { public: Date()//无参构造函数 { _year = 2017;//构造函数函数体内赋值 _month = 8; _day = 8; } Date(int year, int month, int day)//带参构造函数 :_year(year) //使用初始化列表赋值 , _month(month) , _day(day) {} //**缺省的构造函数只能有一个。** //Date(int year = 1900, int month = 1, int day = 1)//全缺省的构造函数 //{ // _year = year; // _month = month; // _day = day; //} private: int _year;//成员变量 int _month; int _day; };
补充:构造函数,说来就是给成员变量进行初始化。而初始化却有两种方法:
初始化列表、构造函数函数体内赋值。
初始化列表可以参考另一篇博客:详解初始化列表
成员变量的初始化顺序
成员是按照他们在类中声明的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的。例如:class Date { public: Date(int year, int month, int day)//带参构造函数 :_month(month)//第二个初始化 , _year(year) //第一个初始化 , _day(day) //第三个初始化 {} private: int _year; //第一个定义 int _month;//第二个定义 int _day; //第三个定义 };
拷贝构造函数
定义
创建对象时使用同类对象来进行初始化,这时所用的构造函数成为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函数。特征:
拷贝构造函数其实就是一个构造函数的重载。拷贝构造函数的参数必须使用引用,使用传值方式会引发无穷递归调用。(为什么?我们保留疑问,后面解答。)
若未显示定义,系统会自动生成默认的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
代码举例
class Date { public: Date()//无参构造函数 { _year = 2017; _month = 8; _day = 8; } Date(const Date& d) //拷贝构造函数 { _year = d._year; _month = d._month; _day = d._day; } private: int _year;//成员变量 int _month; int _day; }; void DateTest() { Date d1; //调用无参的构造函数 Date d2(d1);//调用拷贝构造函数 Date d3 = d1;//调用拷贝构造函数 }
思考:
拷贝构造函数的参数必须使用引用,使用传值方式会引发无穷递归调用。为什么?答:如果采用传值得形式,把形参拷贝到实参会调用拷贝构造函数。那么就会形成无休止的递归。因此C++的标准不允许拷贝构造函数传值参数,而必须是传引用或者常量引用。在Visual Studio和GCC中,都将编译出错。
为什么下面的对象可以直接访问类的使用成员??
Date(const Date& d) //拷贝构造函数 { _year = d._year; _month = d._month; _day = d._day; }
答:
在类的成员函数中可以直接访问同类对象的私有/保护成员。
C++的访问限定符是以类为单位的,也就是说在这个单位内的成员可以相互访问。
什么时候调用拷贝构造函数?
1.当用一个类的对象初始化该类的另一个对象。void DateTest() { Date d1; //调用无参的构造函数 Date d2(d1);//调用拷贝构造函数 Date d3 = d1;//调用拷贝构造函数 }
2.如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。
void fun(Date d) {} void Test() { Date d1; fun(d1);//函数的形参为类的对象时,当调用函数时,拷贝构造函数被调用. }
3.如果函数的返回值是类的对象,函数执行完成返回调用者时。
Date fun() { Date d; return d;//函数的返回值是类的对象,返回函数值时,调用拷贝构造函数 }
析构函数
定义
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即为析构函数(destructor)特征
析构函数在类名加上字符~析构函数无参数无返回值
一个类有且只有一个析构函数。若未显示定义,系统自动生成缺省的析构函数。
对象生命周期结束时,C++编译系统会自动调用析构函数。
注意析构函数体内并不是删除对象,而是做一些清理工作。
代码举例
在函数退出前打一个断点,可以看出对象生命周期结束时,会调用析构函数。class Date { public: Dat d19b e()//无参构造函数 { cout << "Date()" <<endl; } ~Date()//析构函数 { cout << "~Date()" << endl; } private: int _year;//成员变量 int _month; int _day; }; int main() { Date d1; return 0; }
析构函数完成清理工作:
class Array { public: Array(int size) { _ptr = (int *)malloc(size*sizeof (int)); } // 这里的析构函数需要完成清理工作(释放内存)。 ~Array() { if (_ptr) { free(_ptr); _ptr = 0; } } private: int* _ptr; };
什么时候调用析构函数
1.对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数);2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
3.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。
变量的声明顺序与析构顺序
成员变量的声明顺序与其析构顺序相反,即,先声明的变量后析构,后声明的变量先析构。class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } }; class B { public: B() { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; } }; int main() { A a; B b; return 0; }
赋值运算符重载函数
定义
赋值运算符的重载是对一个已存在的对象进行拷贝赋值。C++的重载运算符,由关键字operator和运算符号共同组成,一般而言C++里只要运算符不含”.”都可以重载。
代码举例
class Date
{
public:
Date()//无参构造函数
{
_year = 2017;
_month = 8;
_day = 8;
}
Date(const Date& d) //拷贝构造函数 { _year = d._year; _month = d._month; _day = d._day; }
Date& operator = (const Date& d)// 赋值操作符的重载
{
if (this != &d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
private:
int _year;//成员变量
int _month;
int _day;
};
分析
1、返回值类型返回类型一般声明为类型的引用,并在函数结尾时返回实例自身的引用(即*this)。这里主要有两个原因:
返回引用可以减少一次拷贝构造和析构函数导致不必要的开销,因为返回值类型不是引用,会创建一个匿名对象,这个匿名对象时个右值,获取return的值。
可以实现连续赋值。
2、参数
参数声明为const且是一个引用。
const 是因为赋值运算,不希望修改原来类的状态,同时可以接受const与非const的参数
引用则避免了拷贝构造函数
3、判断是否是传入实例与当前实例是同一个,保证自赋值的安全如果相同,直接返回可以减少不必要的操作,同时防止指向的同一资源一起被销毁。
4、赋值前,释放自身的内存。
取地址操作符重载
Date* operator&() { return *this; }
const修饰的取地址操作符的重载
const Date* operator&() const { return *this; }
函数后边的const表明在函数体中不能改变对象的成员,函数的返回值是指向常对象的指针。
相关文章推荐
- C++类静态成员与类静态成员函数详解
- C++类的默认成员函数
- C++类和对象——四个默认成员函数+运算符重载
- C++基础:C++类中默认的6个函数,及深拷贝和浅拷贝
- c++类中六个默认成员函数
- C++——4个默认成员函数详解
- 详解c++中类的六个默认的成员函数
- C++类和对象.四个默认成员函数(赋值运算符重载)
- C++类和对象及其默认成员函数
- C++类静态成员与类静态成员函数详解
- C++中类的6个默认成员函数
- C++类静态成员与类静态成员函数详解
- 【C++缺省函数】 空类默认产生的6个类成员函数
- 基础备忘:C++类静态成员与类静态成员函数详解
- C++中的四个默认成员函数与运算符重载详解
- [原]C++空类产生哪些成员函数 || C++类可以自动生成的6个成员函数
- C++类静态成员与类静态成员函数详解
- 详解c++中类的六个默认的成员函数
- C++空类产生哪些成员函数 || C++类可以自动生成的6个成员函数
- C++空类产生哪些成员函数 || C++类可以自动生成的6个成员函数