根据之前介绍的内容:在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值,我们之前使用的构造函数都叫做函数体内赋初值
class Date
{
public:
Date(int year = 2024, 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 = 2024, 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 = 2024, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day) //这三个可以在这,也可以在函数体内
,_ref(year)
,_n(1) //这两个必须在这里
{
}
//两个地方可以混着用,这样也行
//Date(int year = 2024, int month = 1, int day = 1)
// : _ref(year)
// , _n(1) //这两个必须在这里
//{
// _year = year;//没有在初始化列表显示出来定义,但是也会定义和初始化,不过内置类型随机值。
//自定义类型调用自己的默认构造函数
// _month = month;
// _day = day;//这三个可以在这,也可以在初始化列表内
//}
private:
int _year;//这些都是声明,还没有空间
int _month;
int _day;
int& _ref;//引用,必须在定义的时候初始化
const int _n;//常量,必须在定义的时候初始化
};
int main()
{
//实例化对象
Date d1;//此时才定义,但是对象整体定义; 那每个成员在哪里定义呢?——就在初始化列表
return 0;
}可以知道的是:在进去函数体之前,定义和初始化都已经完成了,函数体进行的只是单纯的赋值操作。
所有的初始化行为都是在初始化列表内完成的。如果在初始化列表里没有出现的话一般是会在初始化列表给他初始化为默认值(随机值或自己给的缺省值)
之前我们也用过缺省值:
class Date
{
public:
//两个地方可以混着用,这样也行
Date(int year = 2024, int month =1, int day = 1)
: _ref(year)
, _n(1) //这里给1,看结果
{
_year = year;
_month = month;
_day = day;//这三个可以进行赋值
}
private:
int _year=1;//只给_year缺省值
int _month;
int _day;
int& _ref;//引用,必须在定义的时候初始化
const int _n=2;//这里给2
};如果在初始化列表里进行了显示地初始化,那就按照列表里进行(最优先); 没有那才会用缺省值;连缺省值都没有那就随机值了。
上述赋值结果:

class Stack
{
public:
Stack(int capacity)
{
//.......
}
//没有默认构造函数了
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
public:
//此时都是自定义类型,但是又没有默认构造函数;或者有但是不想用。就要自己初始化
MyQueue()
:_s1(4)//自己显示地初始化
, _s2(5)
{
}
private:
Stack _s1;
Stack _s2;
};如果自定义类型没有默认构造函数。解决方案:
class Stack
{
public:
Stack(int capacity=3)
{
cout << "调用了Stack的默认构造函数";
//.......
}
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
public:
private:
Stack _s1;
Stack _s2;//这俩可调用Stack类的默认构造
int _size = -1;//给了缺省值
//此时可以简单点理解:该类的默认构造函数对于自定义就去调用他们的,对于内置使用缺省或随机值
};class Stack
{
public:
Stack(int capacity=3)
{
cout << "调用了Stack的默认构造函数";
//.......
}
//没有默认构造函数了
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
public:
MyQueue()//有没有效果一样,没写就按照默认构造函数那老一套
{ } //写了初始化列表一定会走,但没有显示的写那也是老一套
private:
Stack _s1;
Stack _s2;//这俩可调用Stack类的默认构造
int _size = -1;//给了缺省值
};
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用 用explicit修饰构造函数,将会禁止构造函数的隐式转换
class A
{
public:
A(int a = 0)
:_a(a)
{ }
private:
int _a;
};
int main()
{
A a1(1);//这样创建对象大家都知道
A a2 = 2;//这样也可以:内置类型对象隐式转换为自定义类型对象
//能这样做,是A的int单参数构造函数支持的
//其实隐式转换中间产生一个临时变量,临时变量是A类型的
const A& ra = 3;//给临时变量起别名,这时临时变量会在引用的作用域结束时销毁
return 0;
}
class Date
{
public:
Date(int year, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{ }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 1, 6);
Date d2=2024;//
//Date d2 = { 2024,1,1 };这样也行
Date d3 = (2024, 1, 1);//这样是逗号表达式子<==> Date d3=1 <==> Date d3=(Date)1 <==> Date d3(1)
return 0;
}
class Date
{
public:
// explicit修饰构造函数,禁止类型转换
Date(int year=1, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{ }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2 = { 2024,2 };//从左到右依次赋值
Date d3 = { 2024,2,2 }; //这样也行
Date d4 = 2024;
return 0;
}
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量; 用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化(不走初始化列表,不属于单个成员。类里声明,类外定义) static静态成员变量:属于整个类,属于这个类所有对象。受访问限定符限制
实际上:静态成员函数和静态成员变量,本质上是受限制的全局变量和全局函数(专属这个类,受类域和访问限定符的限制)
#include<iostream>
using namespace std;
class A
{
public:
A()//无参构造
{
count++;
}
A(A& a)//拷贝构造
{
count++;
}
static int count;//类内声明,属于整体(公有)
};
int A::count = 0;//类外定义,就类似于成员函数声明和定义分离
int main()
{
A aa;
cout << A::count << endl;//正常大家会想到这样访问
cout << aa.count << endl;//这样也可以,类比调用成员函数:告诉编译器去那个类里找
}此时是公有,那如果是私有。要怎么访问呢??? 对于count都在类外定义了,为什么不能直接访问呢? 这样就直接以成员函数类比就行
using namespace std;
class A
{
public:
A()//无参构造
{
count++;
}
A(A& a)//拷贝构造
{
count++;
}
int getCount()
{
return count;
}
private:
static int count;//类内声明,属于整体(私有)
};
int A::count = 0;//类外定义
int main()
{
A aa;
cout << aa.getCount()-1 << endl;//因为为了得到count而特地创建了一个对象来调用get函数,要-1;
}现在count是私有了,就定义了一个
getCount函数来得到。但是:为了得到count而特地创建了一个对象来调用get函数(还是有点不合适) 对于对象调用成员函数意义:1. 是告诉编译器getCount在A类里 2. 另一个是传this指针 而编译器在编译阶段遇到变量或者函数,都会去找出处,向上找和全局找(也是命名空间和类域起作用原因)
class A
{
public:
A()//无参构造
{
count++;
}
A(A& a)//拷贝构造
{
count++;
}
static int getCount()//静态成员函数,没有this指针,所以不能访问非静态成员变量
{
return count;
}
private:
static int count;//类内声明,属于整体(公有)
};
int A::count = 0;//类外定义
int main()
{
A aa;
cout << A::getCount()-1 << endl;//可以直接用类名调用
cout << aa.getCount << endl;//这样也行
} 根据上面也可总结出一些特性:
类名::静态成员 或者 对象.静态成员 来访问实际上:静态成员函数和静态成员变量,本质上是受限制的全局变量和全局函数(专属这个类,受类域和访问限定符的限制)
这次就先到这里啦,下次类与对象的内容也要告一段落了,感谢大家支持!!!