类和对象下(初始列表,隐式类型转换,静态成员变量)

1.为什么要用初始化列表

首先了解初始化列表,就要了解下什么是函数体内初始化
代码

class Date
{
public:
	Date(int year = 1, 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 = 1, int month = 1,int day = 1)
		:_year(year)
		, _month(month),
		_day(day)
	{
		
	}
private:
	int _year;
	int _month;
	int _day;
};

这就是初始化列表,初始化列表可以和函数体内初始化一起·用,比如列表初始化了year,month,你用函数体内初始化day是没问题的

我们了解了这二种初始化后,我们就来了解下什么时候用初始化列表
因为有些成员只能在定义初始化
图片
在这里插入图片描述

可以看到在这种情况下,用函数体内初始化,就不行了会报错

如果是初始化列表就不会出现这种问题了
图片
在这里插入图片描述
还有一个用初始化列表的地方,类调用自定义类型,这时候有二种办法,让编译器通过,一,创建一个构建函数,二,用初始化列表
代码

#include<iostream>
using namespace std;


int num = 10;
class A
{
public:
	A(int a = 0)
	:_a(a)
	{

	}
private:
	int _a;
};


class Date
{
public:
	Date(int year, int n, int a)
		:_n(n)
		, _num(num)
		,_aa(a)
	{
		_year = year;
	}
private:
	//定义时初始化
	//也可以不初始化,后面在赋值
	int _year;
	//只能在定义初始化
	const int _n;
	int& _num;
	A _aa;
};

用初始化列表就可以显示初始化了,
int main()
{
	Date d1(2020, 9, 26);
	return 0;
}

总结:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    (1)引用成员变量
    (2)const成员变量
    (3)自定义类型成员(该类没有默认构造函数)
    其他的成员可以在初始化列表,也可以在函数体内初始化
    建议:尽量用初始列表初始化

出个问题 下面代码会打印什么

class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{}

	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};

int main() {
	A aa(1);
	aa.Print();
}

这道题应该很多人的第一反应是打印1 1,毕竟1传过去给a,a初始化a1,a1初始化给a2
但是这道题的答案是1 随机值
因为初始化顺序是是按声明的顺序来初始化的,所以一开始1传给a ,a赋给a2,然后a1没赋值随机值
运行代码
在这里插入图片描述

2.explicit关键字的隐式类型转换

代码

#include<iostream>
using namespace std;


class Date
{
public:
	Date(int year)
		:_year(year)
	{
		cout << "year " << endl;

	}
	Date(const Date& d)
	{
		cout << "const Date&d " << endl;
	}
private:
	int _year;
};

int main()
{
	Date d1(2022);
	Date d2 = 2022;
}

打印结果
在这里插入图片描述
第二个写法是隐式类型转换的转换,把2022转换成Date类型赋值给d2
别看这里二个好像调用都是拷贝构造,但其实第二个不是,比较老一点的编译器是构造+拷贝,现在比较新的编译器都是合二为一
而我为什么讲隐式类型转换呢?c++有个关键字explicit
加上explicit的运行结果
在这里插入图片描述
可以看到报错,不能转换类型

3.静态成员变量

1.概念

全局变量是不好的因为在一些库里面可能会出现名字冲突,而这时就出现了静态成员变量static
静态成员变量写法

#include<iostream>
using namespace std;


class A
{
public:
	A()
	{
		++_count1;
	}

	A(const A& aa)
	{
		++_count2;
	}

//private:
	// 静态成员变量属于整个类,属于类的所有对象
	static int _count1;
	static int _count2; // 声明
};

// 定义
int A::_count1 = 0;
int A::_count2 = 0;

A Func(A a)
{
	A copy(a);
	return copy;
}

int main()
{
	A a1;
	A a2 = Func(a1);
	// 静态成员变量属于整个类,属于类的所有对象
	cout << a1._count1 << endl;
	cout << a2._count1 << endl;

	cout << a1._count2 << endl;
	cout << a2._count2 << endl;

	cout << A::_count1 << endl;
	cout << A::_count2 << endl;

}

因为静态成员变量属于整个类,属于类的所有对象,所以要有定义,上面的那种写法是属于公有写的,如果是私有上面代码是不通过的
不过通过这种静态成员变量,避免使用了全局变量,这就是静态成员变量的优势之一
那么怎么调用私有的静态成员变量
在类里面创建一个函数
代码

#include<iostream>
using namespace std;


class A
{
public:
	A()
	{
		++_count1;
	}

	A(const A& aa)
	{
		++_count2;
	}
	// 成员函数也可以是静态,static成员函数没有this指针
	static int GetCount1()
	{
		return _count1;
	}

	static int GetCount2()
	{
		return _count2;
	}

private:
	// 静态成员变量属于整个类,所以类的所有对象
	static int _count1;
	static int _count2; // 声明
};

// 定义
int A::_count1 = 0;
int A::_count2 = 0;

A Func(A a)
{
	A copy(a);
	return copy;
}

int main()
{
	A a1;
	A a2 = Func(a1);

	cout << a1.GetCount1() << endl;
	cout << a2.GetCount2() << endl;

	cout << A::GetCount1() << endl;
	cout << A::GetCount2() << endl;
}

不过用静态成员变量要注意的是,static成员函数没有this指针
证明图片
在这里插入图片描述
可以看到static成员函数不能访问成员变量,因为里面没有this指针,所以就报错了

2.特性

  1. 静态成员为所有类对象所共享,不属于某个具体的实例
  2. 静态成员变量必须在类外定义,定义时不添加static关键字
  3. 类静态成员即可用类名::静态成员或者对象.静态成员来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值

4.C++11 的成员初始化新玩法

在这里插入图片描述
给缺省值,这里初始化是在初始化列表
变成这样
在这里插入图片描述
但是静态成员变量是不能给缺省值的
图片
在这里插入图片描述
接下来我们就用静态成员变量做道题
题目链接
描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0 < n \le 2000<n≤200
进阶: 空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n)

题目解析:
这题可以用构造函数来做,没创建一个变量都会去调用一次构造函数



class num 
{
public:
    num()
    {
        _ret+=_i;
        _i++;
    }
    static int GetRet()
    {
        return _ret;
    }
private:
    static int _ret;
    static int _i;
};

int num::_ret=0;
int num::_i=1;

class Solution {
public:
    int Sum_Solution(int n) {
        num a[n];
        return a[0].GetRet();
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值