5.初始化表
1)语法
class 类名{
类名(形参表):成员变量1(初值),成员变量2(初值),...{}
};
2)多数情况下,使用初始化表和在构造函数中对成员变量进行初始化操作,并没有太大的区分,两种形式可以任选,但是有些特殊的场景需要使用初始化列表:
3)必须显示使用初始化列表的场景
①如果有类类型的成员变量(成员子对象),而该类又没有无参构造(必须有参构造),则必须使用初始化表来完成该成员子对象的初始化操作
#include <iostream>
using namespace std;
class A{
public:
A(int i){
cout << "A(int)" << endl;
m_i = i;
}
int m_i;
};
class B{
public:
B(void):m_a(123){
cout << "B(void)" << endl;
}
A m_a;//成员子对象
};
int main(void)
{
B b;
cout << b.m_a.m_i << endl;//123
return 0;
}
②如果类中有const或者引用型的成员变量,必须在初始化表显示的初始化,因为const和引用必须在定义时赋初值
#include <iostream>
using namespace std;
int num=20;
class A{
public:
A(void){
m_c = 10;
m_r = num;
}
const int m_c;
int& m_r;
};
int main()
{
A a;
cout << a.m_c << "," << a.m_r << endl;
}
//运行结果:
/*
initlist3.cpp: In constructor ‘A::A()’:
initlist3.cpp:6:9: error: uninitialized const member in ‘const int’ [-fpermissive]
A(void){
^
initlist3.cpp:11:19: note: ‘const int A::m_c’ should be initialized
const int m_c;
^
initlist3.cpp:6:9: error: uninitialized reference member in ‘int&’ [-fpermissive]
A(void){
^
initlist3.cpp:12:14: note: ‘int& A::m_r’ should be initialized
int& m_r;
^
initlist3.cpp:7:16: error: assignment of read-only member ‘A::m_c’
m_c=10;
^
*/
4)成员变量的初始化顺序和初始化表的顺序无关,而是由成员变量的声明顺序决定,不要使用一个成员变量去初始化另一个成员变量
注:以无参的方式创建对象是,不要加(),因为编译器会认为是函数的创建,不是对象的创建
A a();//a会被认为是一个A类型返回值的函数的声明
A a; //以无参的方式创建对象
十五、this指针和常成员函数
1.this指针
1)类中的构造函数和成员函数(包括析构函数)都有一个隐藏的当前类类型的指针参数,名为this,在成员函数中,访问类中的其他成员,其本质都是通过this指针来实现的
①对于普通的成员函数,this指针指向的就是调用该函数的对象
②对于构造函数,this指针指向的是正在创建的对象
#include <iostream>
using namespace std;
class User{
public:
/*User(const string& name,int age)
:m_name(name),m_age(age){
cout << "构造函数:" << this << endl;
}*/
//通过this指针区分成员变量和形参变量
User(const string& m_name,int m_age){
this->m_name = m_name;
this->m_age = m_age;
}
void print(void){
cout << m_name << ',' << m_age <<
endl;
cout << this->m_name << ',' <<
this->m_age << endl;
}/*编译以后
void print(User* this){
cout << this->m_name << ',' <<
this->m_age << endl;
}*/
private:
string m_name;
int m_age;
};
int main(void)
{
User u1("马超",30);
cout << "&u1:" << &u1 << endl;
User u2("黄忠",50);
cout << "&u2:" << &u2 << endl;
u1.print();//User::print(&u1);
u2.print();
}
2)需要使用this指针的场景
①区分作用域
②从成员函数中返回调用的自身
③从类的内部销毁对象
参考代码:
#include <iostream>
using namespace std;
class Counter{
public:
Counter(int count=0):m_count(count){}
//Counter& add(Counter* this)
Counter& add(void){
++m_count;
//this指向调用对象
//*this就是调用对象自身
return *this;//返回自引用
}
void destroy(void){
//...
cout << "this=" << this << endl;
delete this;//对象自销毁
}
int m_count;
};
int main(void)
{
Counter c;
c.add().add().add();
cout << c.m_count << endl;//3
Counter* pc = new Counter(10);
pc->add();
cout << pc->m_count << endl;//11
//delete pc;
cout << "pc=" << pc << endl;
pc->destroy();//(*pc).destroy()
return 0;
}
2.常成员函数
1)在一个普通的成员函数后面加上const修饰,这个成员函数就是常函数
返回类型 函数名(形参表) const {//函数体}
2)常成员函数中的this指针是常指针,不能在常成员函数中修改其他成员变量的值
#include <iostream>
using namespace std;
class A{
public:
A(int data = 0):m_data(data){}
void print(void) const {//常函数
cout << m_data++ << endl;
//cout << const_cast<A*>(this)
// ->m_data++ << endl;
}
/*void print(const A* this){
cout << this->m_data++ << endl;
}*/
private:
mutable int m_data;
};
int main(void)
{
A a(100);
a.print();//100
a.print();//101
}
注:被mutable关键字修饰的成员变量,可以再常函数中被修改
3)非const对象既可以调用常函数也可以调用非常函数,但是常对象只能调用常函数,不能调用非常函数
#include <iostream>
using namespace std;
class A{
public:
//void func1(const A* this){
void func1(void) const {
cout << "常函数" << endl;
//func2();//error
}
//void func2(A* this)
void func2(void) {
cout << "非常函数" << endl;
//func1();//ok
}
};
int main(void)
{
A a1;
a1.func1();//A::func1(&a1),A*
a1.func2();//A::func2(&a1),A*
const A a2 = a1;
a2.func1();//A::func1(&a2),const A*
//a2.func2();//A::func2(&a2),const A*
const A* pa = &a1;//pa:常指针
pa->func1();//A::func1(pa)
//pa->func2();
const A& ra = a1;//ra:常引用
ra.func1();
//ra.func2();
}
注:常对象包括常引用和常指针
4)函数名和形参表相同的成员函数,其长版本和非常版本也可以构成重载关系,常对象调用常版本,非常对象调用非常版本
#include <iostream>
using namespace std;
class A{
public:
void func(void) const {
cout << "func常版本" << endl;
}
void func(void) {
cout << "func非常版本" << endl;
}
};
int main(void)
{
A a1;
a1.func();
const A a2 = a1;
a2.func();
}
十六、析构函数
1)语法
class 类名{
~类名(void){
//主要负责清理对象声明周期中的动态资源
}
};
1)函数名一定是“~类名”
2)没有返回值,没有参数
3)析构函数不能被重载,一个类里面只能有一个析构函数
#include <iostream>
using namespace std;
class A{
public:
A(void){
cout << "A(void)" << endl;
}
~A(void){
cout << "~A(void)" << endl;
}
};
class B{
public:
B(void){
cout << "B(void)" << endl;
}
~B(void){
cout << "~B(void)" << endl;
}
A m_a;
};
int main(void)
{
B b;
return 0;
}
2.当对象被销毁时,该对象的析构函数自动被执行
1)栈对象当其离开作用域时,析构函数被右花括号“}”调用
2)堆对象,在delete时调用析构函数
3.如果类自己没有定义析构函数,那么编译器会为该类提供一个缺省的析构函数
1)对基本类型的成员变量,什么也不做
2)对类类型的成员变量(成员子对象),自动调用相应类的析构函数
4.对象的创建和销毁过程
1)创建
①分配内存
②构造成员子对象(按声明顺序)
③执行构造函数的代码
2)销毁
①执行析构函数代码
②析构成员子对象(按声明逆序)
③释放内存