1、构造函数
只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。但有时候编译器不能为类合成默认构造函数,例如类中包含一个其他类类型的成员且这个成员没有默认构造函数,那么编译器将无法初始化该成员。当然还有一些其他情况,将在13章中介绍。
c++11中可以通过在参数列表后写上 =default;来让编译器生成构造函数。
2、struct和class的唯一区别是默认的访问权限,前者默认public,后者默认private。
3、可变数据成员
通过在变量的声明中加入mutable关键字可以使这个变量能被修改,即使是在一个const成员函数内。
class A
{
public:
void fun()const;
private:
mutable int a;
};
void A::fun()const
{
++a; //合法 虽然fun是个const函数 但是a是个mutable变量
}
4、友元
每个类负责控制自己的友元类或友元函数,友元关系不存在传递性。
5、类的作用域
在类的外部定义成员函数时,函数返回值类型和函数名都不在类的作用域内
class A
{
typedef int index;
index getidx();
};
index A::getidx() //错误 index未定义 应该用A::index
{
return 1;
}
一般来说内层作用域可以重新定义外层作用域中的名字,即使改名字已经在内层作用域总使用过,然而在类中,如果成员使用了外层作用域中的某个名字,而改名字代表一种类型,则类不能在之后重新定义改名字(有些编译器可以通过):
typedef double Money;
class A
{
public:
Money fun() { return bal; } //使用外层作用域的Money
private:
typedef int Money; //书上说这里是错误的 重定义了 在linux上g++编译的确报错了 但是vs上是可以的
Money bal{5};
};
6、构造函数初始值列表
如果类的一个成员是const或者引用或者属于牟总未提供默认构造函数的类类型,则必须通过构造函数初始值列表为这些成员提供初值。
初始值列表初始化成员的顺序与成员在类中定义的顺序一致,而与它在初始值列表中出现的位置无关。
7、隐式的类类型转换
只允许一步的类类型转换。编译器只会自动地执行一步类型转换
class A
{
public:
A() {};
A(string& str) :a(0), s(str) {};
void aprint(const A& ra) { cout << ra.s << endl; }
int a;
string s;
};
void test()
{
A a;
string str = "aaaa";
a.aprint("aaaa"); //错误 aaaa->string是一步转换 string到A是一步转换
a.aprint(str); //正确
a.aprint(string("aaaa")); //错误 按照书上说的应该可以 但是编译失败 不知道为什么
a.aprint(A("aaaa")); //错误 按照书上说的应该可以 但是编译失败 不知道为什么
}
8、explicit
构造函数声明前加explicit可以阻止隐式转换,explicit只允许出现在类内的构造函数声明处。
当用explicit关键字声明构造函数时,它将只能以直接初始化的形式使用,不能将explicit用于拷贝形式的初始化过程,而且编译器不会在自动转换过程中使用该构造函数。
9、constexpr关键字
10、类的静态成员
1)静态成员函数不与任何对象绑定在一起,他们不包含this指针,作为结果,静态成员函数不能声明成const的。
对于为什么静态成员函数不能声明为const,我是这么理解的:它不含this指针是前提,普通成员函数的const意思是不能修改它的普通成员变量,其实是把const作用到了this指针身上。而静态成员函数是不知道归属于对象的普通成员变量的,更谈不上去修改它,所以即使有const也没意义,其实关键在于const是作用到this指针的,静态成员函数没有this指针。
突然有个想法,const是作用与this指针的,而静态成员变量不属于某个对象,那const的成员函数是否能修改静态成员变量呢?
class A
{
public:
A() {};
void fun() const;
static int a;
int b{ 10 };
};
int A::a = 5;
void A::fun()const
{
a++;
//b++; b是对象的成员 由于const的限制不能修改
cout << a << " " << b << endl;
}
void test()
{
A a;
cout << a.a << " " << a.b << endl;
a.fun();
}
事实证明是可以的,cosnt成员函数是可以修改可变(mutable)或静态(static)成员
2)静态成员能用于某些场景,而普通成员不行
class Bar
{
public:
// ...
private:
static Bar mem1; //正确 静态成员可以是不完全类型 不完全是指 这里的Bar还只有声明没有定义
Bar* mem2; //正确 指针和引用成员可以是不完全类型
Bar mem3; //错误 数据成员必须是完全类型
};
3)静态成员可以作为默认实参,普通成员不行
class Bar
{
public:
// a表示一个在类中稍后定义的静态成员
Bar& fun(int b = a);
private:
static const int a;
};