C++之class深度总结
在C++中,class是一种用户自定义的数据类型,并且可以将数据和函数捆绑在一起,形成一个逻辑实体。class是C++面向对象编程(OOP)的基础,提供了将数据和函数捆绑的方式,让代码更加的模块化、提升了代码的可重用性和易读性。
一、C++ class的定义
class是C++中一个重要的关键字,用它来定义类型,其形式如下:
class ClassName{
public:
// 公共的行为和属性
protected:
// 被保护的行为和属性
private:
// 私有的行为和属性
};
1.1 访问控制
class可以通过访问控制符来限制对成员的访问;C++提供了三种控制级别:public、protected和private。默认情况下,class的成员是私有的,只允许类内部的成员函数(和友元类/函数)访问,但是可以通过使用访问控制符来进行控制调整。
1.2 class的三大特性
C++的class有三大特性:封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。
封装: class支持封装,意思就是可以藏逻辑的细节,只提供必要的接口(API)给外部调用。通过将数据私有化并提供公有的访问函数,class通过控制数据的访问操作,从而提高数据安全性。
继承: class可以通过继承来生成新的类(即派生类)。子类(派生类)可以继承父类(基类)的成员(包括数据成员和成员函数),并且可以添加新的成员或覆盖父类的成员。
多态: class的多态性是指允许使用基类的指针或引用来指向派生类的对象,并且可以再运行时动态绑定到正确的函数。多态性是通过虚函数或纯虚函数来(即关键字virtual修饰的函数)实现的。
1.3 成员函数
class可以包含成员函数(即方法或操作),用于处理内部的成员数据。这些成员函数可以访问和修改对象内部的数据,从而执行特定的操作。
1.3.1 构造和析构函数
class中的构造函数和析构函数,用于类对象的初始化和销毁。构造函数在创建对象时被调用,而析构函数则是在对象销毁时被调用,用来释放资源等销毁操作。
class TestClass{
public:
// 构造函数
TestClass();
// 析构函数
~TestClass();
};
何时使用关键字 explicit ?
explicit可以修饰只有一个参数的构造函数,或者有默认参数的多参数构造函数。当构造函数被explicit修饰后,该构造函数只能显式调用,不能用于隐式类型转换。例如:
class TestClass{
public:
explicit TestClass(int value){}
// 或者
explicit TestClass(int value, int value2 = 0,...){}
// 或者
explicit TestClass(int value = 1,...){}
};
int main(){
TestClass obj = 1; // 错误,不能隐式转换
TestClass obj2(2); // 正确,显式调用
}
explicit的作用和重要性:
- 防止隐式类型转换: explicit关键字可以防止编译器自动将一个类型转换为另一个类型,从而避免潜在的错误和精度丢失。
- 提高代码的可读性: 通过明确指定类型转换,可以使代码更加清晰易懂,减少因隐式转换导致的错误。
- 避免意外结果: 隐式转换可能会导致意外的结果,使用explicit可以避免这种情况,确保代码能按预期运行。
1.3.2 关键字inline
inline作为C++中的关键字,在函数声明或定义中,在函数返类型前加上关键字inline(这样可以把函数指定为内联函数)。关键字inline的作用主要是用来解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题。需要注意的是,被inline修饰的函数的逻辑不能太复杂,做好不要超过10行代码,不包含循环语句等。
class TestClass{
public:
inline int add (int a, int b){
return a + b;
}
};
1.3.3 虚函数与纯虚函数
1.3.4 重载
重载是在同一个作用域(同一个类内)内存在多个同名函数或操作符的能力,只要它们的参数类型或参数个数不同即可,可以执行多种类型的操作。
class TestClass{
public:
TestClass(double r = 0.f, double i = 0.f):
real(r),
imag(i){
}
// 函数重载
void display(int a){
std::cout << "Display int: " << a << std::endl;
}
// 函数重载
void display(std::string& a){
std::cout << "Diaplay string: " << a << std::endl;
}
// 运算符重载
TestClass operator+(const TestClass &b){
TestClass tmp;
tmp.real = this->real + b.real;
tmp.imag = this->imag + b.imag;
return tmp
}
private:
double real, imag;
};
需要注意:
- 参数类型和数量:这是区分重载函数的关键。参数顺序、类型或数量必须不同;
- 返回类型:仅当参数列表不同时,返回类型不影响重载决议。
- 友元函数:如果操作符重载需要访问lei的私有成员,通常定位为友元函数。
- 构造函数重载:类可以有多个构造函数,只要它们的参数列表不同。这是构造函数重载的一个常见用法。
- 模板和重载:可以将模板和重载结合,创建灵活的通用代码。例如,使用模板函数来处理不同类型的数组元素。
1.3.5 static
在类的内部声明静态函数时,需要用关键字static修饰,但是在类外部定义函数时就不需要了。因为static成员是类的组成部分但不是任何对象的组成部分,所以有以下几个特点:
- static函数没有this指针;
- static函数不能被声明为const(将成员函数声明为const就是chengnuo不会修改该函数所属的对象);
- static成员函数也不能被声明为虚函数。
// .hpp头文件
class TestClass{
public:
TestClass();
static int add(int value);
private:
static int v;
};
// .cpp 源文件
int TestClass::add(int value){
return TestClass::v + add;
}
1.4 成员变量
class中包含各种数据成员,也可称为类的属性或字段,它们主要用于存储类的属性或状态。它们的数据类型有浮点型、整型、bool型等。
1.4.1 static
static数据成员可以声明为任意类型,可以是常量、引用、数组、类类型等等。
static数据成员必须在类定义的外部定义,并且在定义时进行初始化。
定义该数据成员时,它的定义是在源文件中:
// .hpp 头文件
class TestClass{
public:
TestClass(){}
private:
static int value;
// 特殊的静态常量整型成员
static const int value2 = 5;
};
// .cpp 源文件
int TestClass::value = 10;
1.5 this指针
在成员函数中有一个附加的隐含形参(即this指针),它由编译器隐含的定义。同时成员函数可以显式使用this指针。
class TestClass{
...
public:
TestClass(){}
TestClass& get(){ return *this; }
};
二、继承
继承(inheritance )是面向对象程序设计中代码重复使用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加类型的功能,新生成的类被称为派生类(或子类)。利用继承这一特性,可以体现出面向对象设计的层次结构,体现了从简单到复杂的认知过程。
class Person{
public:
string name;
int age;
};
class Student: public Person{
public:
int _major;
int _id;
};
“Student“是派生类的名称
“:”表示要继承
“public”表示继承的方式,继承的方式有public、protected、private三种方式
“Person”是基类
继承方式和访问限定符的关系,如下表所示:
类成员/继承方式 | public | protected | private |
---|---|---|---|
public成员 | public | protected | private |
protected成员 | protected | protected | private |
private成员 | 不可见 | 不可见 | 不可见 |
总结:
- 基类中的private成员,虽然也被派生类继承,但是在派生类中是不可见的(即不能去访问)。
- 继承时,若不指定继承方式,class默认的继承方式为private,而struct默认的继承方式为public。
- 继承的权限排序:public>protected>private。
三、友元
友元,它是允许一个类将其非公有成员的访问权限授予指定的函数或类;不过友元破坏了class的封装性。友元有两种即友元类和友元函数;友元可以卸载class内的任何位置,建议将友元的声明放在类定义的开始或结尾。
关键字friend就是用来声明友元函数的。
class FirendTestClass{
public:
void test(int value);
};
void test(int value);
class TestClass{
public:
// 友元类
friend class FirendTestClass;
// 友元函数
frient void FirendTestClass::test(int value);
frient void test(int value);
};
需要注意的:
1. 友元是单向性的;
2. 友元不具备继承性。
扩展
- 一个空类的size为1。
class EmptyClass{
};
std::cout << sizeof(EmptyClass) << std::endl;