目录

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* this

	void 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指针,所以就不能访问非静态的成员变量。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐