本文已参与「新人创作礼」活动,一起开启掘金创作之路。
深入构造函数
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
class Date{
public:
Date(int year = 1900, 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 = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day) //参数列表
{}
private:
int _year;
int _month;
const int _day;
};
- 每个成员变量在初始化列表中只能出现一次。(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
、const成员变量
、类类型成员
(该类没有默认构造函数)
class A{ //类
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B{
public:
B(int a, int ref)
:_aobj(a)
, _ref(ref)
, _n(10)
{}
private:
A _aobj; // 类类型成员 ->没有默认构造函数
const int _n; // const
int& _ref; // 引用
};
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
class Time{
public:
Time(int hour = 0)
:_hour(hour)
{
cout << "Time()" << endl;
}
private:
int _hour;
};
class Date{
public:
Date(int day)
{}
private:
int _day;
Time _t;
};
int main() {
Date d(1);
system("pause");
return 0;
}
输出结果:Time()
- 初始化列表中的初始化顺序是:成员变量在类中声明次序,与其在初始化列表中的先后次序无关。
class Array{
public:
Array(int size)
:_size(size)
, _array((int*)malloc(sizeof(int)*_size))
{}
private:
int* _array; //先初始化此成员
int _size;
};
explicit关键字
class Date{
public:
explicit Date(int year)
:_year(year)
{}
private:
int _year;
int _month;
int _day;
};
void TestDate(){
Date d1(2018);
d1 = 2019; // 用一个整型变量给日期类型对象赋值
}
d1 = 2019;
这一语句,用一个整型变量给日期类型对象赋值,实际编译器背后会用2019
构造一个匿名对象,然后用匿名对象给 d1
对象进行赋值。
上述代码可读性不是很好,用explicit
修饰构造函数,将会禁止单参构造函数的隐式转换。
报错 E0349: 没有与这些操作数匹配的"="操作符
static成员
声明为static
的类成员称为类的静态成员,用static
修饰的成员变量,称之为静态成员变量,用static
修饰的成员函数,称之为静态成员函数。
静态的成员变量一定要在==类外进行初始化==。
特性:
- 静态成员为所有类对象所共享,不属于某个具体的实例。
- 静态成员变量必须在类外定义,定义时不添加
static
关键字。 - 类静态成员就可用
类名::静态成员
或者对象.静态成员
来访问。 - 静态成员函数==没有隐藏的
this
指针==,不能访问任何非静态成员。 - 静态成员和类的普通成员一样,也有
public
、protected
、private
3种访问级别,也可以具有返回值,const
修饰符等参数。
Q1
:那么静态成员函数可以调用非静态成员函数吗?Q2
:非静态成员函数可以调用类的静态成员函数吗?A
:根据上面所说的特性4
,因为静态函数中没有this
指针,所以很容易得到结论:
类的非静态成员函数可以调用类的静态成员函数,类的静态成员函数不可以调用类的非静态成员函数。
static的修饰特性:
- 修饰局部变量:改变局部变量的生命周期为整个程序运行期间。
- 修饰全局变量:修改其链接属性,将其作用域限制在当前源文件。
- 修饰函数:改变链接属性,将其作用域限制在当前源文件。
- 修饰成员变量:成员变量属于这个类,属于所有对象,使用类名可以直接访问。
- 修饰成员函数:成员函数没有this指针,属于整个类,属于所有对象,使用类名可以直接调用。
C++11新玩法
C++11
规定:非静态成员变量,可以在成员声明时,直接初始化。
class B{
public:
B(int b = 0)
:_b(b)
{}
int _b;
};
class A{
public:
void Print(){
cout << a << endl;
cout << b._b << endl;
cout << p << endl;
}
private:
// C++11 :非静态成员变量,可以在成员声明时,直接初始化。
int a = 10;
B b = 20;
int* p = (int*)malloc(4);
static int n;
};
int A::n = 10;
int main(){
A a;
a.Print();
return 0;
}
实现一个类,计算程序中创建出了多少个类的对象
class A{
public:
A() { ++_scount; }
A(const A& t) { ++_scount; }
static int GetACount() { return _scount; }
private:
static int _scount;
};
测试代码:
int A::_scount = 0;
void TestA(){
cout << "测试开始:" << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << "共计:" << A::GetACount() << endl;
}
int main() {
TestA();
system("pause");
return 0;
}
样例结果:
测试开始:0
共计:3