C++体验之”二“——this指针以及六个默认的成员函数

上次已经总结过一啦,现在来看一下“二”的总结吧。

—————六个默认的成员函数——————


一、构造函数

1、概念:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只调用一次,保证每个数据成员都一个合适的初始值
例如:

class Date
{
   public:
   Date(int year, int month, int day)
  {
     _year = year; 
     _month = month;
     _day = day;
   }
private:
   int _year;
   int _month;
   int _day;
};
int main()
{
   Date d(2018, 12, 24);
   return 0;
}

2、构造函数特性
(1)函数名与类名相同

(2)没有返回值

(3)新对象被创建时,由编译器自动调用,且在对象的声明周期内仅调用一次

(4)构造函数可以重载,实参决定了调用那个构造函数

(5)无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个

(6)有初始化列表(可以不用)

(7)如果没有显式定义时,编译器会合成一个默认的构造函数

(8)构造函数不能用const修饰(为什么?)
const是可以修饰类的成员函数,但是该函数不能修改数据成员。构造函数也属于类的成员函数,但是构造函数是要修改类的成员变量,所以类的构造函数不能申明成const类型的。

(9)构造函数不能为虚函数(为什么?)
虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。

3、对象初始化

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个”成员变量”后面跟一个放在括号中的初始值或表达式。

注意:

(1)每个成员在初始化列表中只能出现一次(为什么?) ——每个变量成员只能初始化一次

(2)初始化列表仅用于初始化类的数据成员,并不指定这些数据成员的初始化顺序,数据成员在类中定义顺序就是在参数列表中的初始化顺序

(3)尽量避免使用成员初始化成员,成员的初始化顺序最好和成员的定义顺序保持一致

提醒:类中包含以下成员,一定要放在初始化列表位置进行初始化:

(1)引用成员变量

(2)const成员变量

(3)类类型成员(该类有非缺省的构造函数)

4、构造函数作用

(1)构造&初始化对象

(2)类型转换
对于单个参数构造函数,可以将其接受参数转化成类类型对象。用explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复

二、拷贝构造函数

1、概念:只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用

例如:

class Date
{
public:
   Date(int year, int month, int day)
   : _year(year)
   , _month(month)
   , _day(day)
   {}

   Date(const Date& d)
   : _year(d._year)
   , _month(d._month)
   , _day(day)
   {}
private:
   int _year;
   int _month;
   int _day;
};

int main()
{
   Date d1(2018, 12, 24);
   Date d2(d2);
}

2、特征
(1)构造函数的重载,构造函数的性质拷贝构造函数均满足

(2)参数必须使用类类型对象引用传递 为什么?

(3)如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化

3、使用场景

(1)对象实例化对象

(2)作为函数参数

(3)作为函数返回值

三、析构函数

1、概念:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类
的一些资源清理和汕尾工作。
例如:

class Array
{
public:
   Array(int capacity = 10)
   , _array(NULL)
   , _capacity(capacity)
   , _size(0)
   {
      _array = (int*)malloc(sizeof(int)*_capacity);
   }

~Array()
 {
    if(_array)
    {
        free(_array);
        _capacity = _size = 0;
     }
 }
private:
    int* _array;
    size_t _size;
    size_t _capacity;
};

2、特性
(1)析构函数在类名(即构造函数名)加上字符

(2)析构函数无参数无返回值

(3)一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数

(4)对象生命周期结束时,C++编译系统系统自动调用析构函数

(5)注意析构函数体内并不是删除对象,而是做一些清理工作

————————this指针————————


一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。

下面我们来看一个关于this指针的经典回答:

当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?
——this是一个指针,它时时刻刻指向你这个实例本身

this指针特性:
(1)this指针的类型:类类型 * const

(2)this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果

(3)this的作用域在“成员函数”(非静态成员)的内部

(4)this指针是“类成员函数”(非静态成员)的第一个默认隐含函数,编译器自动维护,类编写者不能显式传递

(5)只有在类的非静态成员函数中才可以使用this指针,其他任何函数都不可以

关于this的一些问题回答:

(1)this指针是什么时候创建的?
this在非静态成员中有意义,作为右值可以直接在编译时确定其存在,运行时无所谓创建。

(2)this指针存放在何处?堆,栈,全局变量,还是其他?
由上一问可知,this指针无需显式储存内存中。只要存储对象的内存位置确定,对应的this指针就被确定了。

(3)this指针如何传递给类中函数的?绑定?还是在函数参数的首参数就是this指针.那么this指针又是如何找到类实例后函数的?
this是通过函数参数的首参数来传递的。this指针是在调用之前生成的。类实例后的函数,没有这个说法。类在实例化时,只分配类中的变量空间,并没有为函数分配空间。自从类的函数定义完成后,它就在那儿。

(4)this指针如何访问类中变量的?
如果不是类,而是结构的话,那么,如何通过结构指针来访问结构中的变量呢?如果你明白这一点的话,那就很好理解这个问题了。
在C++中,struct是一种类类型,
注意:struct和class只有一个区别的:class的成员和继承默认的访问控制权限是private,而struct是public。this是class或public的对象的指针。

(5)我们只有获得一个对象后,才能通过对象使用this指针,如果我们知道一个对象this指针的位置可以直接使用吗?
this指针只有在非静态成员中才有意义。获得一个对象后,不需要在类外部使用this对其操作。应当注意this是一个右值(方法的一个隐式参数),不存在所谓的this的“位置”,只是this表示了对象的存储位置而已。&this违反语义规则,是错误的用法,不会编译通过。

(6)每个类编译后,是否创建一个类中函数表保存函数指针,以便用来调用函数?
一般来说,对于类成员函数(不论是静态还是非静态的成员函数)都不需要创建一个在运行时的函数表来保存。只有虚函数才会被放到函数表中。
但是,即使是虚函数,如果编译器能明确知道调用的是哪个函数,编译器就不会通过函数表中的指针来间接调用,而是可以直接调用该函数

猜你喜欢

转载自blog.csdn.net/paranoid_cc/article/details/80719505