类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。数据抽象是一种以来于接口(interface)和实现(implementation) 分离的编程技术。
接口与实现分离
接口方在头文件中。 实现方在cpp文件中。
使用预编译指令,避免多次引入头文件
#ifndef ABC_H
#define ABC_H
class ABC{
};
#endif
构造函数
默认构造函数
- 如果没有显示定义构造函数,则编译器默认地隐式地定义一个默认构造函数。合成地默认构造函数以此规则初始化类地数据成员:有初始值用初始值来初始化;没有的默认初始化。
- 合成地默认构造函数不可靠:内置类型或复合类型(指针、数组)成员,当为定义初始值时, 其值将是未定义的。
- 类中包含 没有默认构造函数 的类类型时,不能初始化。
class A{
A() = default; //显示地要求编译器生成默认构造函数
};
- 使用默认构造函数时不要()
A a(); //定义了一个函数
A a; //使用默认构造函数构造了一个对象
构造函数初始值列表
必显式地初始化的成员,必须通过构造函数初始值列表为其初始化,初始化的顺序与他们在类定义中出现的顺序一致,与初始值列表中出现的顺序无关。
class A{
public:
A(int ii):ri(ii), i(ii){} //i先被初始化,ri后被初始化
private:
const int i;
const int ri;
};
委托构造函数(delegating constructor):使用它所属类的其他构造函数执行自己的初始化过程。优先执行受委托的函数函数体,再执行委托者的函数体。
class A{
public:
A(int a, b);
A(int a) : A(a,0); //委托构造函数
};
隐式的类类型转换-转换构造函数
只接收一个参数的构造函数->定义了转换为此类类型的隐式转换机制->转换构造函数(converting constructor)
- 编译器只会自动地执行一次转换
- 可将构造函数声明为explicit来阻止隐式转换。但还是可以强制地进行。
class A{
public:
A(int a);
explicit A(double b);
}
A a = 10
A b = 10.0; //错误
A b = A(10.0); //正确
友元
使类允许其他类或函数访问它的非公有成员。
(部分编译器允许在尚无友元函数的初始声明的情况下声明友元)
- 友元关系不可传递
- 类和非成员函数的声明不是必须出现在它们的友元声明之前
- 友元函数可以在类内部定义,但也必须在类外部声明该函数从而使该函数可见。
class A{
friend void function();
friend class B;
friend void C::function();
};
拷贝
不主动定义,编译器将替我们合成->将对对象的每个成员执行拷贝。(类需要分配类对象之外的资源时,不能正常工作)。
类型成员
类内定义类型别名。
同样受public/private限制
class A{
public:
typedef int INT;
using LL = long long;
};
explicit关键字
C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
explicit用于阻止隐式的类型转换,如:
class A{
public:
explicit A(int i);
};
A a = 1; //编译将失败
const关键字-常量成员函数
- 修饰了隐式this指针的类型
- 修改成员变量的成员函数-修改函数(mutator)
- 只进行检查不修改成员变量的成员函数-访问函数(accessor) 在成员函数后加const,来表明其是访问函数
class A{
public:
int get() const;
};
在标记为const的成员函数中修改成员变量,将编译错误
基于const的重载
常量对象不可调用非常量版本的函数;非常量对象虽然可以调用常量版本的函数,但非常量版本的函数是更好的匹配。
class A{
public:
void function() const;
void function();
};
const A a;
a.function(); //->调用非常量版本的
const A ca;
ca.function(); //调用常量版本的
operator[]返回值的问题
class A{
......
};
A from;
A to;
现在的目的是将from[i]复制到to[i]
to[i] = from[i];
如果operator[]返回常量引用,则to[i]不能出现在等号左边。
如果operator[]返回普通引用,那么在编译中就会出现诸如from[i] = to[i]的表达式,这样的设计是不够优秀的。
可使用const标识
class A{
public:
const A &operator[](int i) const;
A &operator[](int i);
};
定义在类内部的成员函数是自动inline的。
可变数据成员 - mutable关键字
变量声明时加入mutable关键字,即使在const成员函数中,也可以改变该成员。
静态成员
- 静态数据成员的初始化必须在类外部进行
- 在外部定义静态成员时不需要重复static关键字,该关键字只能出现在类内部。
- 静态数据成员可以是不完全类型
- 静态成员可以做默认实参(非静态成员不能作默认实参)