目录
一,初始化列表
初始化列表用于初始化类成员变量的特殊语法,出现在构造函数的参数列表之后,由冒号隔开;
- 初始化列表是在构造函数体前初始化成员变量;
- 初始化是在定义的同时赋值且只赋值一次,而构造函数体内是先定义后赋值;
初始化列表
- :成员变量(初始值),成员变量(初始值),...
- 冒号开始,逗号分隔数据成员列表,后跟括号为初始值或表达式;
class B
{
public:
B(int a, int b)
:_aa(a) //构造函数成员变量初始化列表,函数体执行之前完成;
{
_b = b; //构造函数体内赋初值,不能称为初始化;
}
private:
int _b;
A _aa;
};
- 成员变量在初始化列表中只能出现一次(即只能初始化一次);
- 类中包括以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量;
- const成员变量;
- 自定义类型成员(无默认构造函数,可理解为显式的调用构造函数);
- 尽量使用初始化列表初始化,因为对于自定义类型成员变量,一定会先使用初始化列表初始化;
- 成员初始化列表直接在构造对象时调用成员变量的构造函数,而赋值初始化则可能涉及额外的复制或移动操作;
- 成员变量在类中,声明次序才是其初始化的顺序,与其在初始化列表中的次序无关;
- 初始化列表可与函数体内赋值混用;
class B
{
public:
B(int a, int b1, int& b2)
:_aa(a) //无默认构造函数,必须在初始化列表初始化
,_b1(b1) //const修饰的变量,必须在初始化列表初始化
,_b2(b2) //引用变量,必须在初始化列表初始化
{}
private:
const int _b1;
int& _b2;
A _aa;
};
初始化列表优点
- 创建对象时可更高效的初始化成员变量,比构造函数体内赋初值可省去一些步骤,提高性能;
- 还允许对constant或引用类型的成员变量进行初始化;
构造函数体内赋值
- 构造函数体内的语句,只能称为赋初值,而不能称为初始化;
- 因为初始化只能一次,而构造函数体内可多次赋值;
class A
{
public:
A(int a = 1)
{
_a = a;
printf("A(int a = %d)\n",a);
}
A& operator = (const A& a)
{
if (this != &a)
_a = a._a;
printf("A& operator = (const A& %d)\n", a);
return *this;
}
private:
int _a;
};
//初始化列表
class B
{
public:
B(int a, int b)
:_b(b),_aa(A(a))
{}
private:
int _b;
A _aa;
};
int main()
{
B b(10, 20);
return 0;
}
//输出结果
//A(int a = 10) //初始化匿名类A(a)
//函数体内赋初值
class B
{
public:
B(int a, int b)
{
_b = b;
_aa = A(a); //A(a)为匿名类,生命周期只在这一行
}
private:
int _b;
A _aa;
};
int main()
{
B b(10, 20);
return 0;
}
//输出结果,先定义后赋值
//A(int a = 1) //初始化类_aa
//A(int a = 10) //初始化匿名类A(a)
//A& operator = (const A & 10) //调用赋值运算符
explicit 关键字
- 对单个参数的构造函数,具有隐式类型转换;
- 用explicit修饰的构造函数,将禁止单个参数的构造函数隐式转换;
- 参考书《深度探索C++对象模型》;
class A
{
public:
A(int a) //explicit A(int a)表示不能隐式类型转换,必须显示调用
:_a(a)
{
cout << "A(int a = 1)" << endl;
}
A(const A& a)
{
cout << "A(const A& a)" << endl;
}
private:
int _a;
};
int main()
{
//先用1构造一个临时对象,在拷贝构造给a,最终vs编译器优化为直接用1构造a
//本质是有个隐式类型转换,int->A
A a = 1; //A(int a = 1)
//如explicit修饰了构造函数,只能使用以下形式
A a1(1); //A(int a = 1)
A a2((A)1); //A(int a = 1)
A a3 = (A)1; //A(int a = 1)
//以上四种形式等价
return 0;
}
二,static成员
- 用static修饰的成员变量,称为静态成员变量;
- 用static修饰的成员函数,称为静态成员函数;
特性:
- 静态成员为所有类对象共享,不属于每个具体的实列;
- 静态成员变量必须在类外定义(const修饰的例外),定义时不添加static关键字;
- 静态成员函数,没有隐藏的this指针,故不可访问任何非静态成员变量/函数;
- 静态成员和类的普通成员一样,也有public、private、protected,也可具有返回值;
- 类静态成员访问,即可用类名::静态成员,也可对象.静态成员;
class A
{
public:
void print() { cout << "print" << endl; }
static int Get() //静态成员函数,不可调用非静态成员变量/函数
{
//_a; 不可调用
//print(); 不可调用
return _count;
}
private:
int _a;
static int _count; //静态成员变量,不可类内定义
static const int _constVar = 1; //const修饰的静态成员变量,可类内定义
};
int A::_count = 1; //需类外定义,且无需static关键字
int main()
{
A a;
//访问形式
A::_count; //静态成员变量属类共享,可通过类作用域限定符访问
a._count;
A::Get(); //静态成员变量属类共享,可通过类作用域限定符访问
a.Get();
return 0;
}
三,C++11的成员变量声明时赋值
C++11支持非静态成员变量,在声明时进行赋初值,但要注意这里不是初始化,是给声明的成员变量缺省值;即构造函数未初始化赋值,会赋此值;
class A
{
public:
A(int a = 0)
{
_a = a;
}
private:
int _a = 10;
};
class B
{
private:
//非静态成员变量,可在成员声明时给缺省值
int _b = 20;
int* p = (int*)malloc(sizeof(int) * 10);
A _a = 20; //单参数,有个隐式转换
//静态成员不在构造函数初始化,要在类外定义初始化
static int _n;
};
int B::_n = 10;
四,友元
友元提供了一种突破封装的方式,但友元会增加耦合度,破坏了封装,所以友元不宜多用;
- 友元函数
- 友元类
友元函数
可直接访问类的私有成员,是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时加friend关键字;
- 友元函数可访问类的私有和保护成员,但不是类的成员函数;
- 友元函数不能用const修饰;
- 友元函数可在类定义的任何地方声明,不受类访问限定符限制;
- 一个函数可以有多个类的友元函数;
- 友元函数的调用与普通函数的调用原理相同;
class A
{
//友元函数
friend void print(const A& a);
public:
A(int a)
:_a(a)
{}
private:
int _a = 10;
};
//类外定义的普通函数,需类内部声明且添加fiend关键字
void print(const A& a)
{
cout << a._a << endl;
}
int main()
{
A a(1);
print(a);
return 0;
}
class B; //前置声明
class A
{
friend void print(const A& a, const B& b);
public:
A(int a)
:_a(a)
{}
private:
int _a = 10;
};
class B
{
friend void print(const A& a, const B& b);
public:
B(int b)
:_b(b)
{}
private:
int _b = 10;
};
//一个函数可以有多个类的友元函数
void print(const A& a, const B& b)
{
cout << a._a << endl;
cout << b._b << endl;
}
int main()
{
A a(1);
B b(2);
print(a, b);
return 0;
}
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员;
- 友元关系是单向的,不具有交换性;
- 友元关系不能传递;
class A
{
friend class B;
private:
int _a = 10;
};
class B
{
public:
void print(const A& a)
{
cout << a._a << endl;
}
private:
int _b = 20;
};
int main()
{
A a;
B b;
b.print(a);
return 0;
}
五,内部类 inner class
如一个类定义在另一个类的内部,此内部的类就叫做内部类;此时这个内部类是一个独立的类,不属于外部类,更不能通过外部类的对象去调用内部类;外部类对内部类没有任何优越的访问权限;
内部类就是外部类的友元类;注意友元类的定义,内部类可通过外部类的对象参数访问外部类中的所有成员,但外部类不是内部类的友元;
特性:
- 内部类可以定义在外部类的public、private、protected,都是可以的;
- 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名;
- sizeof(外部类) = 外部类,和内部类没有任何关系;
class A
{
private:
int _a = 10;
public:
class B
{
public:
//内部类,天生是A的友元
void print(const A& a)
{
cout << a._a << endl;
}
private:
int _b = 20;
};
};
int main()
{
A a;
//受外部类类域即访问限定符限制
A::B b;
b.print(a);
return 0;
}