一、初始化列表
1.初始化列表
1.实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种方式,就是初始化列表,初始化列表的使用方式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。初始化列表是指在成员变量定义的地方进行初始化,每个成员变量只能在初始化列表中出现一次
2.引用成员变量,const修饰的成员变量都需要在定义的时候初始化,所以必须使用初始化列表;没有默认构造函数的自定义类型初始化时需要传值也必须使用初始化列表
3.C++11支持在成员变量声明的位置给出缺省值,这个缺省值是给初始化列表的,当初始化列表中没有该成员时可以使用
4.成员变量初始化的顺序是按照声明的顺序进行的
5.每个构造函数都有初始化列表,每个成员变量都会走一遍初始化列表,所以初始化时尽量选择初始化列表
6.是初始化列表的成员:使用初始化列表进行初始化
不是初始化列表的成语:
(1)有缺省值,使用缺省值进行初始化
(2)没有缺省值:
①内置类型:取决于编译器,大概率是一个随机值
②自定义类型:有默认构造函数就使用默认构造函数,没有默认构造函数时会报错
2.代码分析
成员变量在进行初始化时,有些功能靠初始化列表是无法全部实现的(如malloc的检查),像stack类,这时候就需要函数体内初始化和列表初始化结合使用
像myqueue,不显示写初始化列表,编译器也会自动走一遍初始化列表,调用它的默认构造函数进行初始化,也可以自己显示写初始化列表
像不含自定义类型的Date类,如果没有初始化列表也没有给缺省值,那么结果如何就取决于编译器;如果没有初始化列表但是给缺省值,就会按缺省值去初始化;如果有初始化列表,就会按照初始化列表初始化
二、类型转换
C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
构造函数前⾯加explicit就不再⽀持隐式类型转换
隐式类型转换过程的实质是,内置类型调用构造函数构造一个类的临时对象,临时对象再调用拷贝构造函数拷贝构造类对象,但是编译器一般都会直接优化成用内置类型直接调用构造函数
隐式类型转换是创建的临时对象具有常性,在使用引用和指针时要加上const修饰,使权限缩小
C++11支持连续多参数的隐式类型转化
三、static成员
1.⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化
2.静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区
3. 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针
4.静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针
5.非静态的成员函数,可以访问任意的静态成员变量和静态成员函数
6.突破类域就可以访问静态成员,可以通过(类名::静态成员)或者(对象.静态成员)来访问静态成员变量 和静态成员函数
7.静态成员也是类的成员,受public、protected、private 访问限定符的限制
8.静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表
1.实现一个类,计算当前类对象创建和销毁的个数
class B
{
public:
B()
{
++_scount;
}
B(const B& d)
{
++_scount;
}
~B()
{
--_scount;
}
//非静态成员函数内可以访问静态成员函数
void func1()
{
Getcount();
}
void func2()
{
}
//静态成员函数 没有this指针 函数体内不能访问非静态成员变量 类外通过类域和访问限定符就可以访问
static int Getcount()
{
//静态成员函数内不能访问非静态成员函数 因为没有this指针可以调用静态成员函数
//func2();
return _scount;
}
private:
//成员变量 —— 每个类都有一个成员变量,存储在对象中
int _a1 = 1;
int _a2 = 2;
//静态成员变量 —— 属于类,是每个类对象共有的,存储在静态区
//不能给缺省值 因为它没有初始化列表
static int _scount;
};
//类内声明 类外定义
int B::_scount = 0;
void func()
{
//静态对象只会创建1次
static B bb3;
cout << __LINE__ << ":" << B::Getcount() << endl; //3
}
B bb1;
使用__LINE__来定位本行,static修饰的成员变量是放在静态区的,是对象共享的,不属于某个对象,不会走初始化列表,也没有缺省参数,需要在类外定义初始化,如_scount
静态成员函数可以不可以调用非静态成员函数,因为调用非静态成员函数时有隐藏的this指针,而静态成员函数不含this指针;非静态成员函数可以调用静态成员函数
静态成元函数和静态成员变量都可以突破类域进行访问,只需要通过类域和访问限定符或者用类对象进行调用即可
一个静态成员对象只能创建一次,如B bb3
2.定义一个类,在类外只能在堆/栈上创建对象
class D
{
public:
static D Getstackobj()
{
D dd1;
return dd1;
}
static D* Getheapobj(int a,int b)
{
D* ptr = new D;
ptr->_d1 = a;
ptr->_d2 = b;
return ptr;
}
void print()
{
cout << _d1 << " " << _d2 << endl;
}
private:
D()
{}
int _d1 = 1;
int _d2 = 2;
};
把类的构造函数限定为私有就可以避免在类外通过其他方式创建函数。限定为私有后,想在类外创建函数,就可以使用静态成员函数来实现,因为静态成员函数通过类域和访问限定符就可以访问到
3.求1+2+3+...+n_牛客题霸_牛客网
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return Sum::GetRet();
}
};
四、友元
1.友元分为:友元函数和友元类,在函数声明或者类声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯
2.外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数
3.友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制
4.⼀个函数可以是多个类的友元函数
5.友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员
6.友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元
7. 友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元
8.友元会增加耦合度破坏封装,所以友元不宜多⽤
五、内部类
1.⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类
2.内部类是⼀个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类
3.内部类默认是外部类的友元类
4.当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了
//内部类
class A
{
public:
class B //内部类是外部类的友元
{
public:
B(int b=1)
{
_b = b;
}
void foo(const A& a)
{
cout << a._a << endl;//OK
}
private:
int _b;
};
A(int a=1)
{
_a = a;
}
//B bb();
private:
int _a;
};
求外部类的大小时不需要考虑内部类,外部类实质上是不含内部类的,外部类定义的对象中也没有内部类,但如果在外部类中创建了内部类的对象,那就要考虑内部类的大小了
内部类的访问需要受外部类类域和访问限定符的限制
内部类可以通过外部类对象对外部类的成员变量进行访问
class Solution {
//内部类
class sum{
public:
sum()
{
_i++;
_ret+=_i;
}
};
public:
int Sum_Solution(int n) {
sum a[n];
return _ret;
}
private:
static int _i;
static int _ret;
};
int Solution::_i=0;
int Solution::_ret=0;
六、匿名对象
类型(实参) 定义出来的对象叫做匿名对象,类型+对象名(实参) 定义出来的叫有名对象
匿名对象⽣命周期只在当前一行,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象
//匿名对象
class C
{
public:
C(int c = 1)
{
_c = c;
cout << "C(int c = 1)" << endl;
}
C(const C& c)
{
_c = c._c;
cout << "C(const C& c)" << endl;
}
~C()
{
_c = 0;
cout << "~C()" << endl;
}
void print()
{
cout << _c << endl;
}
private:
int _c;
};
匿名对象具有常性,在使用引用和指针时必须加上const修饰,修饰后的匿名对象生命周期延长到函数局部域
七、对象拷贝时的编译器优化
现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传参 过程中可以省略的拷贝
如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理
1.传值传参和传引用传参
2.值返回和引用返回
3.构造优化
注意:连续拷贝+赋值重载是无法优化的