【C++入门】类与对象(3)
目录
1. const成员
1.1 &重载函数
1.2 const函数使用的时机
2. 再谈构造函数
2.1 初始化列表的意义?
2.2 初始化列表题目
2.3 三种创建对象的方式(栈)
2.4 explicit关键字
3. static成员
1. const成员
首先看四个问题:
①const对象可以调用非const成员的函数吗?
②非const对象可以调用const成员函数吗?
③const成员函数内可以调用其它的非const成员函数吗?
④非const成员函数内可以调用其它的const成员函数吗?
这里很像绕口令,不过没关系,我们可以先看下面的代码,最后再来解答这四个问题。
class Date
{
private:int year;int month;int day;public:void f1() // Date* this{f2(); // f2(this)}void f2() const // const Date* this 权限缩小{}void f3() {} // Date* thisvoid f4() const // const Date* this{f3(); // f3(this) 权限放大,错误}
};
上面有四个成员方法,我们在上一期讨论过,f1()的调用是因为形参中有一个this指针,例如f1(Date* this),这个指针很重要, void f2() const 其实等价于 void f2(const Date* this),保证this指针指向的对象是不能被改变。
首先我们看f1,调用f2,f2(Date* this),此时this指针权限是最大的,因为没有const修饰,传入我们可以看到f2的形参是有const修饰的,其实就是将大权限变成小权限的形参,这里只是进行权限缩小,所以没有问题;
我们再看f3,f3的形参没有const修饰,所以此时形参的权限最大,f4的this指针是const Date* this,作为实参传入f3的形参,此时权限进行了放大,这里一定会有问题。
我们再来回答四个问题:
①const对象可以调用非const成员的函数吗? NO,this指针权限进行放大
②非const对象可以调用const成员函数吗? YES,this指针权限缩小
③const成员函数内可以调用其它的非const成员函数吗? NO,this指针权限放大
④非const成员函数内可以调用其它的const成员函数吗? YES,this指针权限缩小
1.1 &重载函数
当我们取地址对象其实是在调用下面的重载函数。
Date* operator&() // 形参Date* this
{return this;
}
当我们调用d2的地址的时候,此时会存在问题,d2的类型是const Date 传入形参中会造成权限扩大,所以我们还可以对上面的&重载函数进行重载,具体如下:
const Date* operator&() const
{return this;
}
1.2 const函数使用的时机
一句话总结,只要成员变量不改变,无脑加const,类似于,void fuc () const ,这样const对象或者非const对象都能调用这个函数。
2. 再谈构造函数
构造函数中给成员变量赋值,除了可以使用构造函数函数体中进行赋值,还可以使用初始化列表,这个方法,具体操作如下:就在方法的后面,成员变量(),中间使用逗号分隔。
Date() :year(10),month(1),day(20)
{}
2.1 初始化列表的意义?
引用成员变量、const 成员变量、没有默认构造函数的对象的初始化必须使用初始化列表。
我们可以理解初始化列表就是成员变量定义的地方。引用和const成员变量以及没有默认构造函数的对象都必须在定义的时候初始化,所以这就很好理解了。
2.2 初始化列表题目
这里看初始化列表的初始化顺序,这里会先初始化a2,因为a2先声明,此时a1的值是随机值,所以a2就是随机值,a1被赋值为1,此时输出1 随机值,选D。
2.3 三种创建对象的方式(栈)
前面我们学过可以使用构造函数进行构造对象,也可以使用拷贝构造函数构造对象,今天来介绍另外一种构造对象的方式,那就是隐式类型转换:
Date d1(1); // 构造函数
Date d2 = d1;// 拷贝构造
Date d3 = 1; // 隐式类型转换 Date tmp(1) -> d3 = tmp;
其实就是创造了一个临时变量,tmp,再使用拷贝构造函数进行初始化对象。类似于:
int i = 10;
double j = i;
/*
事实上是const double tmp = i;这里的const是因为临时变量具有常性
j = tmp;如果使用
double& j = i;
就会报错,这是因为其实将 const double 给了引用,如果想要不报错
const double& j = i;
*/
如果参数不止一个,例如构造函数中有三个形参,在C++11中可以使用下面的代码进行构造,也是隐式类型转换。
Date(int year,int month ,int day):a(year),ref(month),c(day)
{}int main(){Date d = { 1,2,3 }; // c++11中可以批量隐式类型转换
}
2.4 explicit关键字
2.3中讲述了Date d = 1;这里其实是隐式类型转换,如果不想让这个隐式类型转换发生,这里可以在构造函数前面加上explicit关键字,阻止隐式类型转换的发生。
3. static成员
设计一个类,计算这个类总计产生了多少个对象。
对象的产生一定是构造函数或者拷贝构造函数产生的,所以可以定义一个静态的成员变量用于计算对象。
class A
{
private:
public:static int cnt;A(){cnt++;}A(const A& a) {cnt++;}};
A f1(A a)
{return a;
}
int A::cnt = 0;// 初始化int main()
{A a1;A a2;f1(a1);f1(a2);cout << a1.cnt << endl;
}
答案是6,因为a1、a2会使用构造函数2次,调用函数的时候传入形参会调用拷贝构造,返回值的时候依然会调用拷贝构造函数,所以1 + 1 + 2 + 2 = 6;
此时将f1的返回值换成引用,那么此时的值就是4,因为返回值就不需要调用拷贝构造了。
使用static修饰成员方法,这个方法没有this指针,所以就不能访问非静态的成员变量。