初始化列表
C++提供了初始化列表语法,可以用于成员属性初始化。
语法规则:
无参构造函数():属性1(值1), 属性2(值2), ...
{
}
有参构造函数(形参1, 形参2, ...):属性1(形参1), 属性2(形参2), ...
{
}
example:写一个怪物类,有怪物id和血量属性,并用初始化列表的形式初始化成员属性
#include <iostream>
using namespace std;
class Monster
{
public:
Monster():m_monsterId(10001), m_blood(1000)
{
}
Monster(const int monsterId, const int blood):m_monsterId(monsterId), m_blood(blood)
{
}
void print_monster_info()
{
cout << "怪物id = " << m_monsterId << ",血量 = " << m_blood << endl;
}
private:
int m_monsterId; //怪物id
int m_blood; //血量
};
int main(int argc, char *argv[])
{
Monster m1;
m1.print_monster_info();
Monster m2(10002, 2000);
m2.print_monster_info();
return 0;
}
类对象作为类成员
C++中的另一个类的对象可以作为类的成员,我们称为该成员为对象成员。那么这时是先构造对象成员,还是先构造该类呢?答案是先构造对象成员,后构造该类,析构函数刚好相反,先析构该类,后析构对象成员。
example:举一个有趣的例子,有一个技能类,一个怪物类,这时怪物会有技能了,所以在设计怪物类时,会有技能类作为怪物类的成员。用来验证另一个类的对象作为类成员时,先构造对象成员,后构造该类。析构则先析构该类,后析构对象成员。
#include <iostream>
using namespace std;
enum SKILL_TYPE
{
SUB_DEST_BLOOD_ADD_SELF_BLOOD = 0
};
int g_line = 0;
class Monster;
class Skill
{
public:
Skill():m_skillType(SUB_DEST_BLOOD_ADD_SELF_BLOOD), m_val(500)
{
g_line++;
cout << g_line << "行:Skill()无参构造函数被调用" << endl;
}
Skill(const int skillType, const int val):m_skillType(skillType), m_val(val)
{
g_line++;
cout << g_line << "行:Skill(const int skillType, const int val)有参构造函数被调用" << endl;
}
Skill(const Skill &s)
{
m_skillType = s.m_skillType;
m_val = s.m_val;
g_line++;
cout << g_line << "行:Skill(const Skill &s)拷贝构造函数被调用" << endl;
}
~Skill()
{
g_line++;
cout << g_line << "行:~Skill()析构函数被调用" << endl;
}
//施法
void spell(Monster &src, Monster &dest);
private:
int m_skillType; //技能类型
int m_val;
};
class Monster
{
public:
Monster():m_monsterId(10001), m_name("怪物"), m_blood(1000), m_skill()
{
g_line++;
cout << g_line << "行:Monster()无参构造函数被调用" << endl;
}
Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal):
m_monsterId(monsterId), m_name(name), m_blood(blood), m_skill(skillType, skillVal)
{
g_line++;
cout << g_line << "行:Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal)有参构造函数被调用" << endl;
}
Monster(const Monster &m)
{
m_monsterId = m.m_monsterId;
m_name = m.m_name;
m_blood = m.m_blood;
m_skill = m.m_skill;
g_line++;
cout << g_line << "行:Monster(const Monster &m)拷贝构造函数被调用" << endl;
}
void setBlood(const int blood)
{
m_blood = blood;
}
int getBlood()
{
return m_blood;
}
void setName(const int name)
{
m_name = name;
}
string getName()
{
return m_name;
}
//释放技能
void spell(Monster &dest)
{
m_skill.spell(*this, dest);
}
~Monster()
{
g_line++;
cout << g_line << "行:~Monster()析构函数被调用" << endl;
}
void print_monster_info()
{
g_line++;
cout << g_line << "行:怪物id = " << m_monsterId << ",名字 = " << m_name << ",血量 = " << m_blood << endl;
}
private:
int m_monsterId; //怪物id
string m_name; //怪物名字
int m_blood; //血量
Skill m_skill; //技能
};
//施法
void Skill::spell(Monster &src, Monster &dest)
{
switch (m_skillType)
{
case SUB_DEST_BLOOD_ADD_SELF_BLOOD:
{
//dest
int destBlood = dest.getBlood();
destBlood -= m_val;
if (destBlood < 0)
destBlood = 0;
dest.setBlood(destBlood);
//src
int srcBlood = src.getBlood();
srcBlood += m_val;
if (srcBlood < 0)
srcBlood = 0;
src.setBlood(srcBlood);
g_line++;
cout << g_line << "行:" << src.getName() << " 攻击了 " << dest.getName() << endl;
g_line++;
cout << g_line << "行:" << src.getName() << "的血量增加到:" << src.getBlood() << endl;
g_line++;
cout << g_line << "行:" << dest.getName() << "的血量减少到 " << dest.getBlood() << endl;
break;
}
default:
g_line++;
cout << g_line << "行:技能类型未处理:" << m_skillType << endl;
}
}
int main(int argc, char *argv[])
{
Monster m1(10001, "雪女", 10000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 1000);
Monster m2(10002, "紫衣仙子", 20000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 2000);
m1.print_monster_info();
m2.print_monster_info();
m1.spell(m2);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/d3dfdc723400e2484a024610f83c90ba.png)
接下来我们再根据打印结果进行代码分析:
Monster m1(10001, "雪女", 10000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 1000);
另一个类的对象可以作为类的成员时,会先构造对象成员,再构造改该类,析构则刚好相反,先析构
该类,再析构对象成员。所以执行该语句,调用构造函数的顺序为:
(1)Skill类有参构造函数被调用
(2)Monster类有参构造函数被调用
Monster m2(10002, "紫衣仙子", 20000, SUB_DEST_BLOOD_ADD_SELF_BLOOD, 2000);
另一个类的对象可以作为类的成员时,会先构造对象成员,再构造改该类,析构则刚好相反,先析构
该类,再析构对象成员。所以执行该语句,调用构造函数的顺序为:
(1)Skill类有参构造函数被调用
(2)Monster类有参构造函数被调用
main函数执行结束时,会调用析构函数释放资源,调用析构函数的顺序为:
(1)m2被析构:Monster类析构函数被调用
(2)m2的m_skill对象成员被析构:Skill类析构函数被调用
(3)m1被析构:Monster类析构函数被调用
(4)m1的m_skill对象成员被析构:Skill类析构函数被调用
静态成员
静态成员包括:
静态成员变量:就是在成员变量前加上关键词static修饰的成员变量
所有对象共享一份数据
可以通过类名进行访问:Monster::ms_counter;
可以通过对象进行访问:Monster m; m.ms_counter;
在编译阶段分配内存
类内声明,类外初始化
有(public,protected,private)访问权限
静态成员函数:就是在成员函数前加上关键字static修饰的成员函数
所有对象共享同一个函数
可以通过类名进行访问:Monster::getMonsterCounter();
可以通过对象进行访问:Monster m; m.getMonsterCounter();
静态成员函数的函数体内只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数
有(public,protected,private)访问权限
example:测试静态成员变量,所有对象共享一份,类内声明,类外初始化,以及两种访问方式
#include <iostream>
using namespace std;
class Monster
{
public:
Monster():m_monsterId(0)
{
ms_counter++;
}
Monster(const int monsterId):m_monsterId(monsterId)
{
ms_counter++;
}
Monster(const Monster &m)
{
m_monsterId = m.m_monsterId;
ms_counter++;
}
~Monster()
{
ms_counter--;
}
static int ms_counter; //怪物个数
private:
int m_monsterId; //怪物id
};
int Monster::ms_counter = 0; //静态成员类外初始化
int main(int argc, char *argv[])
{
//1.静态成员用类名进行访问
cout << "ms_counter = " << Monster::ms_counter << endl;;
//2.静态成员用对象进行访问
Monster m1;
cout << "ms_counter = " << m1.ms_counter << endl;
Monster m2;
cout << "ms_counter = " << m2.ms_counter << endl;
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/ce890c36a6f88515e3f17ac1bb191e88.png)
需要注意的是,静态成员变量也有(public,protected,private)访问权限的,如果我们将以上代码中的,静态成员变量声明为private权限,类外将不能对静态成员变量进行访问
![](https://i-blog.csdnimg.cn/blog_migrate/6a34570aa53b87eae56e055e9f054638.png)
example:测试静态成员函数所有对象共享一份,静态成员函数的函数体内只能访问静态成员变量或静态成员函数,不能访问非静态成员变量和非静态成员函数。以及两种访问方式
#include <iostream>
using namespace std;
class Monster
{
public:
Monster():m_monsterId(0)
{
ms_counter++;
}
Monster(const int monsterId):m_monsterId(monsterId)
{
ms_counter++;
}
Monster(const Monster &m)
{
m_monsterId = m.m_monsterId;
ms_counter++;
}
~Monster()
{
ms_counter--;
}
static int getMonsterCounter()
{
return ms_counter;
}
static void setMonsterCounter(const int counter)
{
ms_counter = counter;
cout << "ms_counter = " << getMonsterCounter() << endl;
//m_monsterId = 90001; //错误:静态成员函数只能访问静态成员变量或静态成员函数
}
private:
static int ms_counter; //怪物个数
int m_monsterId; //怪物id
};
int Monster::ms_counter = 0; //静态成员类外初始化
int main(int argc, char *argv[])
{
//1.静态成员用类名进行访问
cout << "ms_counter = " << Monster::getMonsterCounter() << endl;;
//2.静态成员用对象进行访问
Monster m1;
cout << "ms_counter = " << m1.getMonsterCounter() << endl;
Monster m2;
cout << "ms_counter = " << m2.getMonsterCounter() << endl;
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/f9a004052bb37a51c24a843abfd21b39.png)
需要注意的是,静态成员函数也有(public,protected,private)访问权限的,如果我们将以上代码中的,静态成员函数声明为private权限,类外将不能对静态成员函数进行访问
![](https://i-blog.csdnimg.cn/blog_migrate/62e29c29c800119ad9416a03ef5a1633.png)
好了,关于C++面向对象编程之三:初始化列表、类对象作为类成员、静态成员,先写到这。