文章目录
- 1.初始化列表
- 什么是初始化列表?
- 初始化列表的 意义及使用
- 2.explicit关键字
- 单参数构造函数(C++98)
- 多参数的构造函数(C++11)(了解)
- 3.static静态成员
- 静态成员变量与静态成员函数
- 静态成员变量
- 静态成员函数
- 4.匿名对象
- 5.友元
- 友元函数
- 友元类
- 6.内部类(C++很少用)
- 7.拷贝对象时的一些编译器优化
1.初始化列表
什么是初始化列表?
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。
例如:
class Date
{
public:
Date(int year, int month, int day)
: _year(year)//以冒号开始
, _month(month)//逗号分隔
, _day(day)//括号中为初始值
{}
private:
int _year;
int _month;
int _day;
};
初始化列表的 意义及使用
(之所以没有默认生成构造函数是因为——默认生成的构造函数对内置类型不处理,但const 变量只有一次被定义的机会,必须要初始化)
- 不管哪个对象调用构造函数,初始化列表是它所有成员变量定义的地方
- 不管是否在初始化列表里有写,编译器每个变量都会在初始化列表被初始化
- 如果 const 变量有缺省值,只有当其他任何地方都没有给初始化值的时候才会用这个缺省值,即没给值才用缺省值
- 注意:
- 每个成员只能初始化 一次
- 三类 必须初始化 的类型:
- const
- &引用
- 没有默认构造函数的自定义类型
class Round
{
public:
Round(int a)//这个不是默认构造函数,默认构造函数时不传参的!!
:_a(a)
{}
private:
int _a;
};
class Bottle
{
public:
Bottle(int a, int ref)
:_rd(a)
, _b(ref)
, _n(13)
{}
private:
Round _rd; // 没有默认构造函数
int& _b; // 引用
const int _n; // const
};
- 初始化的顺序是类中成员变量定义的顺序
class A
{
public:
A(int a)
:_a1(a)
, _a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
output:-1 随机值
- 建议尽量使用初始化列表
2.explicit关键字
单参数构造函数(C++98)
分析一段代码:
class A
{
public:
A(int a)//单参数构造函数
:_a1(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)//拷贝构造函数
:_a1(aa._a1)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
void Testexplicit()
{
A aa1(1);
A aa2 = 1;
A& variable = 13;
const A& variable = 13;
}
int main()
{
Testexplicit();
return 0;
}
🔊 explicit
不允许隐式类型转换:
多参数的构造函数(C++11)(了解)
class A
{
public:
A(int a1, int a2)//多参数构造函数
:_a1(a1)
, _a2(a2)
{}
private:
int _a1;
int _a2;
};
void Testexplicit()
{
A aa3(1, 2);
A aa4 = { 1,2 };
}
3.static静态成员
- 如何统计 类的对象的个数?
基本思路:创建 类的对象一定回去调用 构造函数 或者 拷贝构造函数
- 定义全局变量统计个数?
- 代码:
int count = 0;
class A
{
public:
A()//构造函数
{
++count;
}
A(const A& aa)//拷贝构造函数
{
++count;
}
private:
int _a1;
int _a2;
};
void TestStatic()
{
A a1, a2, a3, a4;
cout << count << endl;
}
- 问题:
- 变量名冲突
报错:变量名 count 与 std 库发生冲突
error C2872: “count”: 不明确的符号
message : 可能是“int count”或 “iterator_traits<_Iter>::difference_type std::count(const _InIt,const _InIt,const _Ty &)”
所以,最好不要全局展开std
- 容易出纰漏
静态成员变量与静态成员函数
静态成员变量
- ✅静态成员变量:属于所有对象
- 静态成员变量的声明和初始化:
class A
{
static int count;//声明
};
int A::count = 0;//定义初始化
注意:静态成员变量声明处不能给缺省值,缺省值时给构造函数初始化用的,作用于非静态成员变量
- 静态成员变量的访问:(以上述代码为例)
①当 静态成员变量 是 public
的
void TestStatic()
{
A a1;
a1.count;
A::count;
A* ptr = nullptr;
ptr->count;
}
- 对象.count:
A a1;a1.count;
——创建对象,通过对象访问成员变量 A::count;
A* ptr = nullptr; ptr->count;
② 通过写函数访问
class A
{
public:
A()//构造函数
{
++count;
}
A(const A& aa)//拷贝构造函数
{
++count;
}
int Getcount()
{
return count;
}
private:
int _a1;
int _a2;
static int count;//声明
};
int A::count = 0;//定义初始化
void TestStatic()
{
A a1;
cout << a1.Getcount() << endl;
}
静态成员函数
void TestStatic()
{
A a1;
cout << a1.Getcount() << endl;
}
👆为了调用这个函数专门要创建一个对象?→如何改进?→『 静态成员函数 』
- 什么是 静态成员函数:(示例)
static int Getcount()
{
return count;
}
- 如何调用静态成员函数:
A::Getcount();
(完整示例:↓)
class A
{
public:
A()//构造函数
{
++count;
}
A(const A& aa)//拷贝构造函数
{
++count;
}
static int Getcount()
{
return count;
}
private:
int _a1;
int _a2;
static int count;//声明
};
int A::count = 0;//定义初始化
void TestStatic()
{
A array[13];
cout << A::Getcount() << endl;
}
ps.
A array[13];
自定义类型的数组,会自动调用13次构造函数。
- 静态函数变量的特点:
静态成员函数没有 this 指针,因此 不能访问非静态成员。
4.匿名对象
- 什么是匿名对象:
class A{};
A();//匿名对象
- 匿名对象的特点:生命周期只在这一行
ps.临时变量和匿名对象都具有 常性
5.友元
友元分为 友元函数和友元类,由于友元 破坏了封装 ,建议能不用就不用。
友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。
示例:
class Date
{
public:
//流插入
//函数声明
friend void operator<<(ostream& out, const Date& d);
private:
int _year;
int _month;
int _day;
};
//函数定义
void operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
//友元函数可以直接访问类的私有成员
}
//函数调用:
cout << d1; 👉 operator<<(cout,d1);
注意:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。 友元关系是单向的,不具有交换性。
6.内部类(C++很少用)
- 概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个 独立(空间独立)的类,它不属于外部类!
- 注意:内部类就是外部类的友元类,但是外部类不是内部类的友元。
- 特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。内部类是独立的!
内部类可以看作是外部类的友元类,只是受外部类的类域限制。
class A
{
public:
class B //内部类
{
public:
void foo(const A& a)
{
cout << k << endl;
cout << a.h << endl;
}
};
private:
static int k;
int h;
};
int A::k = 1;
int main()
{
A::B b;//类B受外部类A的类域限制
b.foo(A());
return 0;
}
7.拷贝对象时的一些编译器优化
1. 构造+拷贝 → 优化为直接构造
当构造和拷贝都在一个表达式里时才能优化:
-
不在一个表达式:
-
在一个表达式:
2. 传引用传参 → 不优化,没有拷贝
3. 传值返回 → 不优化(如下代码,构造和拷贝不能结合起来)
A func()
{
A aa;//构造 “构造”
return aa;//返回一份临时拷贝 “拷贝”
}
4.返回值接收:拷贝+拷贝 → 优化成一个拷贝
class A{};
A func()
{
A aa;//构造 “构造”
return aa;//返回一份临时拷贝 “拷贝”
}
int main()
{
/*func(A());*/
A aa1 = func();//拷贝接收
//func()“拷贝”+aa1"拷贝构造" → “拷贝”+“拷贝”
//如果写成:
A aa2;
aa2 = func();//这样写不优化
return 0;
}
5. 返回匿名对象
class A{};
A func()
{
return A();
}
构造匿名对象 A()
→ “构造”
返回一个自定义对象,生成一份临时拷贝 → “拷贝”
构造+拷贝 → 优化为直接构造
sum.提高效率:
1.接收返回值对象,尽量用拷贝接收(4.)
2.返回值尽量用匿名对象(5.)
3.传参尽量用引用(2.)
END