目录
一.初始化列表初始化的构造函数
1.之前给对象初始化是在构造函数体内进行初始化,初始化列表就是以冒号开始,用逗号分割的数据成员列表,在每一个数据后有一个小括号,小括号内可以放初始值或者表达式。
2.每个成员只能在初始化列表中出现一次,可以理解为在初始化列表进行变量的定义。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{
}
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024,7,24);
d1.print();
return 0;
}
3.因为const类型的变量,引用只能在定义的时候才能初始化,所以这两个必须进行初始化列表初始化。还有无默认构造函数的自定义类型必须在初始化列表初始化。
4.C++11可以在成员变量声明的时候给缺省值,当该成员变量没有显示写初始化列表时,就会走缺省值。
5.尽量都走初始化列表初始化,因为不管你有没有显示的写出来,每一个成员变量都会走初始化列表初始化,如果在声明的地方给了缺省值,就会用这个缺省值初始化。但是如果没有给缺省值又分两种情况,第一个是内置类型,这个取决于编译器,可能会初始化,也可能给随机值;第二个是自定义类型,如果该类有默认构造函数就走默认构造函数初始化,当没有默认构造时,编译器就会报错。
class Time {
public:
Time(int hour=1)
:_hour(hour)
{
}
//非默认构造,如果不在Date的初始化列表初始化就会报错
/*Time(int hour)
:_hour(hour)
{
}*/
private:
int _hour;
int _min;
int _second;
};
class Date
{
public:
Date(int& xxx, int year, int month, int day)
:_year(year)
, _month(month)
//, _day(day)
, x(1)
, xx(xxx)
{
}
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
//这里给缺省值并不是指开了空间,只是未走初始化列表时,给编译器的初始化建议
int _year=1;
int _month=1;
int _day=1;
//const,引用,有无默认构造
const int x;
int& xx;
Time t;
};
6.初始化的顺序是按照声明成员变量的顺序来初始化的。
下代码运行结果是什么?
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2 = 2;
int _a1 = 2;
};
int main()
{
A aa(1);
aa.Print();
}
答案是1,随机值。
是因为先声明的_a2,后声明的_a1,所以走初始化列表初始化时,先用_a1初始化_a2,但是此时_a1还并没有被a或1初始化,_a1是随机值,所以_a2被初始化为随机值,_a1被传的参数1初始化为1。
二.类型转换
1.C++支持将内置类型隐式转换为自定义类型,与构造函数参数有关
2.构造函数前⾯加explicit就不再⽀持隐式类型转换
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
{}
/*explicit A(int a)
:_a1(a)
{}
*/
A(int a1,int a2)
:_a1(a1)
,_a2(a2)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a1 = 1;
int _a2 = 1;
};
int main()
{
//单参构造
//先将2构造一个临时对象,再用临时对象拷贝构造aa
//构造加拷贝构造会优化为直接构造
A aa=2;
aa.Print();
//多参构造函数
A aaa = { 3,3 };
aaa.Print();
}
三.static成员
1.static修饰的成员变量就是静态成员变量,静态成员变量不能在类内初始化,只能在类外初始化。
2.静态成员变量为所有对象共有,不存在变量中,而是存在静态区。
3.static修饰的函数是静态成员函数,静态成员函数中无this指针。
4.静态成员函数,不能够访问非静态成员变量,因为无this指针。
5.非静态成员函数可以访问静态成员变量和静态成员函数。
6.可以突破类域访问静态成员,通过类名::静态成员或者对象.静态成员,来访问静态成员变量和静态成员函数。
7.静态成员同样也受private,public,protect访问限定符的限制。
8.静态成员不能够在声明的地方给缺省值,因为静态成员不存在对象中,不需要走初始化列表初始化。
#include<iostream>
using namespace std;
class A {
public:
A()
{
_second++;
}
A(A& a)
{
_second++;
}
~A()
{
_second--;
}
//静态成员函数
static int get_second()
{
return _second;
}
private:
//静态成员变量
static int _second;
};
//只能在类外给静态成员变量初始化
int A::_second = 0;
int main()
{
A a1,a2;
//类名::成员函数
cout<<A::get_second() << endl;
A a3(a1);
cout << A::get_second() << endl;
//对象.成员函数
cout << a1.get_second() << endl;
//_second是private的,所以不能直接访问需要在公有属性中提供一个接口
//cout<<A::_second
return 0;
}
牛客题
class sum{
public:
sum()
{
_sum+=_i;
_i++;
}
static int get_sum()
{
return _sum;
}
private:
static int _i;
static int _sum;
};
int sum::_i=1;
int sum::_sum=0;
class Solution {
public:
int Sum_Solution(int n) {
sum x[n];
return sum::get_sum();
}
};
下面代码构造和析构的顺序是什么?
C c;
int main()
{
A a;
B b;
static D d;
return 0
;
先构造的后析构,全局最先创建,局部静态到语句的时候才创建。
四.友元
1.友元提供了一种突破访问限定符的方法,友元分为友元函数和友元类,只需要在函数或类的声明前加一个friend就可以,并且声明放在一个类中。
2.外部的友元函数可以访问内部的私有和保护成员,友元函数并不是类的成员函数。
3.友元函数可以在类的任何地方声明不受限制。
4.一个函数可以是多个类的友元函数。
#include<iostream>
using namespace std;
// 前置声明,都则A的友元函数声明编译器不认识B
class B;
class A
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
// 友元声明
friend void func(const A& aa, const B& bb);
private:
int _b1 = 3;
int _b2 = 4;
};
//一个函数可以是多个类的友元函数
void func(const A& aa, const B& bb)
{
cout << aa._a1 << endl;
cout << bb._b1 << endl;
}
int main()
{
A aa;
B bb;
func(aa, bb);
return 0;
}
5.友元类中的成员函数是另一个类的友元函数,可以访问另一个类的私有和保护成员。B是的友元类,则B中的函数可以访问A的私有和保护成员。
6.类的友元不具有传递性和交换性。
7.友元会提高代码的耦合性,但是会降低封装,所以友元少用。
#include<iostream>
using namespace std;
class A
{
// 友元声明
friend class B;
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
public:
void func1(const A& aa)
{
cout << aa._a1 << endl;
cout << _b1 << endl;
}
void func2(const A& aa)
{
cout << aa._a2 << endl;
cout << _b2 << endl;
}
private:
int _b1 = 3;
int _b2 = 4;
};
int main()
{
A aa;
B bb;
bb.func1(aa);
bb.func2(aa);
return 0;
}
五.内部类
1.一个类定义在另一个类的类中,就叫内部类。内部类是一个独立的类,内部类只受类域和访问限定符的限制,并不存在于外部类的对象中。
2.内部类是外部类的友元类。
3.内部类也是一种封装,如果A和B的关系密切,A中的方法供B类使用,那就可以将A类定义为B的内部类(A类的成员函数可以访问B类的私有成员),如果放到private或public的位置,那A就是B类的专属友元类。
#include<iostream>
using namespace std;
class A {
public:
//内部类B,默认为A的友元
class B {
public:
//为A提供方法,可以访问A中的成员变量
void func1(A&aa)
{
cout << aa._a1 << endl;
cout << _b1 << endl;
}
void func2(A& aa)
{
cout << aa._a2 << endl;
cout << _b2 << endl;
}
private:
int _b1 = 2;
int _b2 = 2;
};
private:
int _a1=1;
int _a2 = 1;
};
int main()
{
//A类大小为8,说明A类的对象中不包含B
cout << sizeof(A) << endl;
A a;
//B类在A的类域,用::,否则编译器报未声明
A::B b;
//通过B访问A中的私有成员变量
b.func1(a);
b.func2(a);
return 0;
}
所以1+2+3+4+...+n可以改写成
class Solution {
public:
class sum{
public:
sum()
{
_sum+=_i;
_i++;
}
};
static int _i;
static int _sum;
int Sum_Solution(int n) {
sum x[n];
return _sum;
}
};
int Solution::_i=1;
int Solution::_sum=0;
六.匿名对象
1.用类型(实参)定义出来的叫匿名对象,用类型 对象名(实参)实例化的对象叫有名对象。
2.匿名对象的生命周期只在当前那一行,通常只需要临时用一下的时候才使用匿名对象。
#include<iostream>
using namespace std;
class A
{
public:
A(int a =0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A aa1;
A(1);
A aa2;
return 0;
}
第二三行就是因为创建的是匿名对象,生命周期只有一行,构造之后到下一行就马上进行析构。
七.对象拷贝时编译器的优化
1.现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传参 过程中可以省略的拷⻉。
2.如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译还会进⾏跨 ⾏跨表达式的合并优化。
#include<iostream>
using namespace std;
class A
{
public:
A(int a = 1)
:_a1(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)
:_a1(aa._a1)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa)
{
_a1 = aa._a1;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
//private:
int _a1;
};
void func1()
{
}
void func3(A aa)
{
aa._a1 *= 10;
cout<< aa._a1<<endl;
}
void func2(A& aa)
{
}
int main()
{
//A aa1;
//func1(aa1);
//A aa2;
//func2(aa2);
//func1(1);
// func1(A(2));
//A aa3;
//A aa4 = aa3;
A aa5;
func3(aa5);
return 0;
}