封装
public、protected、private的区别
- public在类内和类外都可以被访问;
- protected和private只能在类内被访问;
- protected可以被子类访问,而private不能;
struct和class的区别
- struct的默认权限为公有;
- class的默认权限为私有;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class C1 { int m_A; };
struct C2 { int m_A; };
int main() {
C1 c1; c1.m_A = 10;
C2 c2; c2.m_A = 10;
system("pause");
return 0; }
|
对象的初始化和清理
构造函数和析构函数
- 我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现;
- 构造函数和析构函数编译器会自动调用;
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用;
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作;
- 构造函数语法:类名(){};
- 构造函数可以有参数,因此可以发生重载;
- 析构函数语法:~类名(){};
- 析构函数不可以有参数,因此不能发生重载;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Person { public: Person() { cout << "Person的构造函数调用" << endl; } ~Person() { cout << "Person的析构函数调用" << endl; }
};
void test01() { Person p; }
int main() { test01();
system("pause");
return 0; }
|
构造函数
构造函数两种分类方式:按参数分为有参构造和无参构造;按类型分为有参构造和无参构造;
1 2 3 4 5 6 7
|
Person p('张三')
Person p = Person('张三');
Person p = '张三';
|
拷贝构造函数(它是一种函数)
1 2 3 4 5
| Person(const Person& p) { age = p.age; cout << "拷贝构造函数!" << endl; }
|
拷贝构造函数调用的时机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<iostream> using namespace std; class Person { public: int p_age; Person() { cout << "调用默认构造函数" << endl; } Person(int age) { p_age = age; cout << "调用有参构造函数" << endl; } Person(const Person& p) { cout << "调用拷贝构造函数" << endl; } ~Person() { cout << "调用析构函数" << endl; } }; void fun1(Person p){} Person fun2() { Person p; return p; } void test03() { Person p1 = fun2(); } int main() { test03(); return 0; }
|
构造函数的调用规则
- 默认情况下,编译器会默认创建三个函数:构造函数,析构函数,拷贝构造函数;
- 在有有参构造函数的情况下,编译器不会创建无参构造函数;
- 在有拷贝构造函数的情况下,编译器不会自动提供构造函数;
深浅拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include<iostream> #include<string.h> using namespace std; class Student { public: string s_name; int* s_age; Student(string name, int age) { s_name = name; s_age = new int(age); } Student(const Student& p) { s_name = p.s_name; s_age = new int(*p.s_age); } ~Student() { if (s_age != NULL) delete s_age; } }; int main() { Student s1("张三", 18); Student s2(s1); cout <<"s1的name:" << s1.s_name << "age:" << *s1.s_age << endl; cout <<"s2的name:" << s2.s_name << "age:" << *s2.s_age << endl; return 0; }
|
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
列表初始化
1 2 3 4 5 6
| class Student{ public: string s_name; int s_age; Student(string name, int age):s_name(name),s_age(age){} };
|
类对象作为类成员
- 当类中成员是其他类对象时,我们称该对象为
对象成员
;
- 构造函数调用顺序:先调用对象成员的构造函数,在调用本类的构造函数;
- 析构函数的调用顺序跟构造函数的调用顺序相反;
静态成员
- 静态成员分为:静态成员变量和静态成员函数;
- 静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成团函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
- 静态成员也是有访问权限的;
1 2 3 4 5 6
|
Person p; p.name;
Person::name;
|
C++模型和this指针
成员变量和成员函数分开存储
在C++中,成员变量和成员函数是分开存储的,只有非静态成员变量才是属于对象的;
this指针的用途
- 当形参和成员变量同名时,可用this指针来区分;
- 在类的非静态成员函数中返回对象本身,可使用return *this;
1 2 3 4 5
| Person(int age) {
this->age = age; }
|
1 2 3 4 5 6
| Person& PersonAddPerson(Person p) { this->age += p.age; return *this; }
|
空指针访问成员函数
- 空指针可以访问成员函数,但是当成员函数中有this时就会报错;
- 为了保持代码的健壮性,可以在成员函数中进行限制;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Person { public:
void ShowClassName() { cout << "我是Person类!" << endl; }
void ShowPerson() { if (this == NULL) { return; } cout << mAge << endl; }
public: int mAge; };
void test01() { Person * p = NULL; p->ShowClassName(); p->ShowPerson(); }
int main() {
test01();
system("pause");
return 0; }
|
const修饰成员函数
常函数:
- 成员函数后加const后我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加const称该对象为常对象;
- 常对象只能调用常函数;
- 常对象可以修改加mutable的成员属性的值;
继承
继承的基本语法
class 子类 : 继承方式 父类
继承后的权限和访问权限
- 继承后的权限是父类中的权限和继承方式中安全性最高的权限为主;
- 访问权限由父类决定,在父类中是private的权限不可被访问;
继承中构造函数和析构函数的顺序
- 构造函数的调用顺序是先调用父类构造函数,再调用子类构造函数;
- 析构函数的调用顺序和构造函数相反;
继承同名成员处理方式
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数;
- 如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域;
1 2
| s.func(); s.Base::func();
|
继承同名静态成员处理方式
和上述方法一致。
多继承语法
class 子类 :继承方式 父类1 , 继承方式 父类2...
多态
虚函数和纯虚函数
1 2 3 4 5 6 7
| virtual void speak() { cout << "动物在说话" << endl; }
virtual void speak() = 0;
|
虚函数一般都是没有用的,所哟一般都用纯虚函数;
抽象类
有纯虚函数的类叫抽象类。
切记,有虚函数的类不一定是抽象类,一定要是纯虚函数,有虚函数的类是可以被实例化的。