构造函数的初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
(初始化列表是构造函数的一部分)
添加位置:在构造函数的{}前面
Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {}
只要调用了构造函数,都会走一次初始化列表,没有的话,就走隐式初始化列表
隐式初始化列表的规则:
对于内置类型,高级的编译器会初始化其为默认值(比如int的搞成0)
对于类类型(即对象成员):
如果该类型有默认构造函数,则会调用默认构造函数;如果没有默认构造函数且未显式初始化,则编译错误。
注意:每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
类中包含以下成员,必须放在初始化列表位置进行初始化:(成员是private时)
1.引用成员变量
2.const成员变量
(因为引用成员变量和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(); } A. 输出1 1 B.程序崩溃 C.编译不通过 D.输出1 随机值//说成随机值也行,但是一般都是0 //在调用构造函数的时候,肯定已经定义对象了,所以已经给_a1分配内存了,不单单只有声明了 解:应该选D
explicit关键字
A aa1(1)和A aa2 = 1还是有一点区别的:
A aa2 =1 是隐式类型转换,整形转换成自定义类型
也就是1构造一个A的临时对象,临时对象再拷贝构造aa2,但是许多编译器都会用2直接构造
注意点:接受单个参数的构造函数才能这么搞:A aa2 =1
接收单个参数的构造函数具体表现:
1. 构造函数只有一个参数
2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
3. 全缺省构造函数
explicit修饰构造函数,用来禁止类型转换
使用举例:
explicit Date(int year)
:_year(year)
{}
static成员
概念:
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。
注意:静态成员变量一定要在类外进行初始化
应用:弥补了全局变量在任意地方都能被改变的缺陷(static成员变量可以替代他)
特性:
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区,程序结束才调用他的析构
(是所有类对象公有的,一个对象的这个静态变量改变,其他对象的这个变量也会随之改变)
2.静态成员变量必须在类外定义(初始化列表也不走),定义时不添加static关键字,类中只是声明
(但是有些时候又不会报错(不建议声明和定义不分离哈),比如:
const static size_t npos = -1
)static成员就算是私有的,在类外也可以定义 用法: 声明时用: static int a; 类外定义:eg: int A::a = 1;
3.类静态成员只可用 类名::静态成员 或者 对象.静态成员 来访问
(不要跟普通的成员函数和成员变量搞混了,这两个只能用对象.成员来访问)
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员也是类的成员,受public、protected、private 访问限定符的限制
非静态成员能否调用静态成员:可以
静态成员能否调用非静态成员不可以
原因:因为没有this指针,但是非静态成员(包括函数)的调用需要this指针
因此: 静态成员函数和静态成员变量一般是配套出现的
友元
友元分为友元函数和友元类,但是友元一般不推荐用,会破坏封装性
友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
friend istream& operator>>(istream& _cin, Date& d); //也就是在普通函数声明前面加个friend istream& operator>>(istream& _cin, Date& d) { _cin >> d._year; return _cin; }
性质:
1.友元函数可以访问类里面的所有成员,但是友元函数不是类的成员函数
2.友元函数不能用const修饰,但是其参数可以用const修饰
(const修饰函数的情况一般是成员函数(因为有this指针))
3.友元函数可以在类定义的任何地方声明,不受类访问限定符的限制
(比如在private那声明和在public处声明一样)
4.一个函数可以是多个类的友元函数
5.友元函数的用法和普通函数的用法相同
本质:友元函数本身并不是类的成员函数,而是被类授予了访问其私有(
private
)和保护(protected
)成员权限的外部函数
友元类
友元类(A)就是在另一个类(B)里面声明A是B的友元
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的所有成员。
举例:friend class Date;
注意点:
1.友元关系是单向的,不具有交换性
2.友元关系不能传递
举例:B是A的友元,C是B的友元,但是不能说C是A的友元
3.友元关系不能继承
内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
注意:
内部类是一个独立的类,它不属于外部类,只是在外部类里面声明了一个类型而已
内部类是外部类的天生友元,但是外部类不是内部类的天生友元
这个内部类的话,C++也很少用
特性:
1.内部类不会占用外部类的存储空间,sizeof(外部类)=外部类的,和内部类没有任何关系
2.内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名(访问其他成员的话要对象)(友元类和函数的话是需要的)
class A { private: static int k; int a; public: class B //B是A的内部类 { void foo(const A& a) { cout << k << endl; cout << a.h << endl; } }; }; int A::k = 1; 内部类的实例化:A::B bb;
3.内部类可以定义在外部类的public、protected、private都是可以的
注意:内部类和外部类都需要使用的成员的话,一般搞在外部类
匿名对象
概念:没有名字的对象
匿名对象也具有常性
举例: class Solution { public: static test(const Solution& b) {} Solution(int b) { _a = b; } int Sum_Solution(int n) {} private: int _a; }; int main() { Solution aa(1);//有名对象 Solution(1);//匿名对象 匿名对象的特殊使用: //Solution的()不可以省 //(无需传参的话也要这个,需要传参的话,就跟有名对象搞法一样去传) Solution().Sum_Solution(20); return 0; }
注意:
有名对象的生命周期在当前函数的局部域
匿名对象的生命周期在当前行,但是匿名对象有续命方法:
const引用可以延长匿名对象的生命周期,生命周期在当前函数的局部域
比如:const Solution& ra = Solution(1); Solution::test(Solution(1)); //如果test不加const和&的话,匿名对象的生命周期就到不了函数里面去 //如果都去了的话,也行,就算拷贝了(这个易忘);单有const或&不行
编译器对连续构造的优化
在一个表达式里(短的那种)连续构造,编译器基本上都会将其优化成一次构造;
多个对象时,只会尽量优化,不一定能优化成一次
(构造和拷贝构造都算构造,拷贝函数是构造函数的一种)当然,这里到底是合几为一要看编译器的做法,
我们要做的是:构造和拷贝构造能合起来搞就合起来搞
比如:假设A是一个类 void Func1(A aa){} A aa1(1); Func1(aa1);//这样编译器就优化不了 Func1(A(1));//这样编译器就会去优化->本为构造+拷贝构造 Func1(1)和A aa1 =1;的过程差不多
作业部分:
设计一个类,在类外面只能在栈上创建对象:
class A
{
public:
static A GetStackObj()
{
A aa1;
return aa1;//这样来限制了只能构造对象在栈里面,每次调用生成的对象不一样
}
private:
A()//把构造函数搞成私密
{}
};
引申:(在类外定义对象)
static A a1; // 存在静态区
A a2; // 存在栈
A* ptr = new A; // 存在堆
牛客 JZ64 求1+2+3+...+n
要求不能使用不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句
这边外加要求不能使用递归和位运算
解法:创建一个类型是类的数组(大小为n),类的构造函数里面ret+=i;i++
ret和i都搞成的是静态成员变量(初始值都是0)
注意获取ret的成员函数要搞成static,因为静态成员调用不了非静态的
在一个cpp文件里面,定义了一个static类型的全局变量,下面一个正确的描述是:(A)
A.只能在该cpp所在的编译模块中使用该变量
B.该变量的值是不可改变的
C.该变量不能在类的成员函数中引用
D.这种变量只能是基本类型(如int,char)不能是C++类型
A的引申:
局部变量中的 static 会修改局部变量的生命周期;
局部变量,全局变量和函数前的 static,只能在当前.cpp文件中使用
即使其他文件链接了,也找不到,因为static修饰之后不进入符号表