1、程序的内存模型
/*
代码区 :
放二进制编译好的代码, 生成exe的可执行程序
存放cpu执行的机器指令
特点:共享,只读
全局区:
全局变量,字符串常量,其他常量, 静态变量
当进程结束时才会死亡
栈区:由编译器自动分配,管理函数的生成与死亡
堆区:由操作者进行
*/
#include <iostream>
using namespace std;
int main() {
return 0;
}
2、栈区
#include<iostream>
using namespace std;
// 不要返回栈区的地址, 因为栈区的局部变量会由编译器自动释放
// 在这里保留了局部变量
int* func() {
int a = 10;
return &a;
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
return 0;
}
3、堆区
#include<iostream>
using namespace std;
int* func() {
// 利用 new 将数据开辟到堆区
// 指针本质也是一个局部变量
// 用局部变量指针来储存全局变量的地址
int* p = new int(10);
return p;
}
int main() {
// 在这里的堆区创建了一个指针来储存来自栈区返回的一个地址
int* p = func();
cout << *p << endl;
return 0;
}
4、new 的基本用法
#include<iostream>
using namespace std;
int* func() {
// 在堆区创建一个数据
// new 返回的是该数据的指针
int* p = new int(10);
return p;
}
void test01() {
int* p = func();
cout << *p << endl;
// 堆区的数据由程序员管理释放
// 如果想释放,用堆区的关键字 delete
// delete 的对象是一个指针
delete p;
// 内存已经释放,再操作为非法操作
}
// 2 在堆区用new开辟一个数组
void test02() {
// 数组中有10个元素
int* arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
for (int i = 0; i < 10; i++) {
cout << arr[i] << "array" << endl;
}
// 释放数组
// 释放数组的是一个中括号,否则释放第一个元素
delete[] arr;
}
int main() {
test01();
// 1 new的基本语法
test02();
return 0;
}
5、引用的基本语法
#include <iostream>
using namespace std;
// 实质,给变量起别名,使不同的变量操作相同的内存空间
int main() {
int a = 10;
int& b = a;
b = 20;
cout << "a=" << a;
return 0;
}
6、引用的注意事项
#include <iostream>
using namespace std;
int main() {
int a = 10;
// 引用必须要初始化
int& b = a;
// 引用一旦初始化后就不可以更改
int c = 20;
b = c; // 赋值操作而不是更改引用
return 0;
}
7、引用做函数参数
#include <iostream>
using namespace std;
// 交换函数
// 值传递, 形参不修饰实参
void swap1(int a, int b) {
int temp = a;
a = b;
b = temp;
}
// 地址传递, 形参修饰实参
void swap2(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 引用传递, 形参修饰实参, 别名和原名一样的
void swap3(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
swap1(a, b);
cout << "swap1 " << a << " and " << b << endl;
swap2(&a, &b);
cout << "swap2 " << a << " and " << b << endl;
swap3(a, b);
cout << "swap3 " << a << " and " << b << endl;
return 0;
}
8、引用做函数的返回值
#include <iostream>
using namespace std;
// 不要返回局部变量的引用
int& test01() {
int a = 10;
return a;
}
// 函数的调用可以作为左值
int& test02() {
// 静态变量存放在全局区
static int a = 10;
return a;
}
int main() {
int& b = test01();
// 第一次正确是应为编译器做了保留, 我这里没有保留
cout << "ref1 = " << b << endl;
int& c = test02();
cout << "ref2 = " << c << endl;
c = 100;
cout << "ref2 = " << test02() << endl;
// 作为左值, 函数的每次调用返回的引用,都是相同的内存地址, 也就是全局变量的相同的指针
test02() = 1000;
cout << "ref2 = " << c << endl;
return 0;
}
9、引用的本质
#include <iostream>
using namespace std;
// 引用的本质就是指针常量
// int& a = a; ===> int* const a = &a;
int main() {
return 0;
}
10、常量引用
#include <iostream>
using namespace std;
void showVal(const int& a) {
cout << "val = " << a << endl;
}
// 用来修饰常量,使其不可以被修改
int main() {
// 编译器会自动创建一个值为10的内存空间,并且会自动生成一个原名,
// 因此我们只能使用 引用的别名
// 同时引用的值不能被修改
const int& a = 10;
// 主要用来修饰形参, 防止误操作
int a = 100;
return 0;
}
11、函数的默认参数
#include <iostream>
using namespace std;
int calculate(int a = 10, int b = 20) {
return a + b;
}
// 当声明有了默认参数时, 函数的实现就不能有默认参数
// 函数的声明和实现只能有一个存在默认参数
// 函数的声明
int func(int a = 10, int b = 10);
int main() {
int a = calculate();
cout << a << endl;
return 0;
}
// 函数的实现
int func(int a, int b) {
return a + b;
}
12、函数的占位参数
#include <iostream>
using namespace std;
// 放一个书包来占位置
// 目前阶段的占位参数用不到
void func(int a, int) {
cout << "this is " << a << endl;
}
// 占位参数可以有默认参数 func(int a = 10, int = 20)
int main() {
func(10, 10);
return 0;
}
13、函数重载的基本用法
#include <iostream>
using namespace std;
// 条件
// 必须在同一个作用域下
// 函数名称相同
// 函数的参数类型,个数,顺序不同
void func() {
cout << "func ----> " << endl;
}
void func(int a) {
cout << "func a ----> " << endl;
}
int main() {
func();
func(1);
return 0;
}
14、函数重载的注意事项
#include <iostream>
using namespace std;
// 引用
void func(int& a) {
cout << "func no const" << endl;
}
void func(const int& a) {
cout << "func const" << endl;
}
// 函数重载遇见默认参数
void func1(int a, int b = 10) {
cout << "func default" << endl;
}
void func1(int a) {
cout << "func no default" << endl;
}
int main() {
int a = 10;
// 引用
func(a);
func(20);
// 默认参数
// 传一个参数时会报错,两个不会报错
func1(10, 10);
return 0;
}
15、类和对象封装属性和行为
#include <iostream>
using namespace std;
// design a circle to calc the arc
const double PI = 3.14;
class Circle {
//访问权限
public:
// 属性
int r;
//行为
double calcArc(){
return 2 * PI * r;
};
};
int main() {
// 实例化对象
Circle c1;
c1.r = 10;
cout << "circle len = " << c1.calcArc() << endl;
return 0;
}
16、设计学生类
#include <iostream>
using namespace std;
class Student {
public:
string name;
int id;
void set_name(string name_) {
name = name_;
}
void show_id_name() {
cout << "name:" << name << "\tid:" << id << endl;
}
};
int main() {
Student student;
student.name = "alex";
student.id = 202031080136;
student.show_id_name();
return 0;
}
17、封装访问权限
#include <iostream>
using namespace std;
// public ----> 类里类外都可以访问
// private ----> 只有类里可以访问 (私有成员变量) ----> 继承的子类不可以访问
// protected ----> 只有类里可以访问 (私有成员变量) ----> 继承的子类可以访问
class Person {
public:
string name;
void set_name(){
name = "alex";
Car = "car";
password = 34121234;
};
protected:
string Car;
private:
int password;
};
int main() {
Person person;
Person* p = &person;
cout << p->name << endl;
return 0;
}
18、class与struct的区别
#include <iostream>
using namespace std;
// struct 默认的权限为 public
// class 默认的权限为私有
int main() {
return 0;
}
19、成员属性的私有化
#include <iostream>
using namespace std;
class Person {
public:
void setName(string name_){
name = name_;
};
string getName() {
return name;
};
int getAge() {
age = 10;
return age;
}
void setLover(string lover_) {
lover = lover_;
}
private:
string name;
int age;
string lover;
};
int main() {
Person person;
person.setName("alex");
cout << "name : " << person.getName() << endl;
cout << "age: " << person.getAge() << endl;
return 0;
}
20、实例:立方体类的设计
#include <iostream>
using namespace std;
class Cube {
public:
void setMeasure(int width, int height, int len) {
_width = width;
_height = height;
_len = len;
}
int* getMeasure() {
return arr;
}
int calcArea() {
return (_width * _height + _height * _len + _len * _width) * 2;
}
private:
int _width;
int _len;
int _height;
int arr[3] = { _width, _height, _len };
};
int main() {
Cube cube;
cube.setMeasure(10, 10, 10);
int* p = cube.getMeasure();
for (int i = 0; i < 3; i++) {
cout << i << "-----" << p[i] << endl;
}
cout << "cube area : " << cube.calcArea() << endl;
return 0;
}
21、示例:点和圆的位置关系
#include <iostream>
#include<math.h>
using namespace std;
struct Point {
int centerX;
int centerY;
void setCenter(int x, int y){
centerX = x;
centerY = y;
};
};
class Circle {
public:
int setR(int r) {
radius = r;
}
int getR() {
return radius;
}
void setCenter(int x, int y) {
point.setCenter(x, y);
}
Point getCenter() {
return point;
}
// 判断点和圆的关系
void isIncicle(Point point_) {
int distance = sqrt(pow((point.centerX - point_.centerX), 2) + pow((point.centerY - point_.centerY), 2));
if (distance == radius) {
cout << "on circle" << endl;
}
else if (distance > radius) {
cout << "out circle" << endl;
}
else if (distance < radius) {
cout << "in circle" << endl;
}
}
private:
int radius = 10;
Point point;
};
int main() {
Circle circle;
circle.setCenter(10, 10);
Point point = { 30, 10 };
circle.isIncicle(point);
return 0;
}
22、构造函数和析构函数
#include <iostream>
using namespace std;
// 构造函数语法
// 析构函数语法
// 不写的话会默认调用空的构造和析构
// 对象的初始化和清理
class Person {
public:
Person();
~Person();
private:
};
// 构造函数, 可以发生重载
Person::Person() {
cout << "构造函数的调用" << endl;
}
// 析构函数, 对象清理的时候调用
Person::~Person() {
cout << "析构函数的调用" << endl;
}
void test01() {
// 栈区上的数据,调用完后会释放
Person person;
}
int main() {
test01();
return 0;
}
23、函数的分类和调用
#include <iostream>
using namespace std;
// 构造函数的分类以及调用
class Person {
public:
int age;
// 普通构造函数
Person();
Person(int a);
// 拷贝构造函数
Person(const Person &p){
// 将传入的人身上所有的2属性都传递给这里
// 将传入的属性
cout << "拷贝函数的构造的调用" << endl;
age = p.age;
};
~Person();
private:
};
Person::Person() {
cout << "无参数构造函数的调用" << endl;
}
Person::Person(int a) {
age = a;
cout << "有参数构造函数的调用" << endl;
}
Person::~Person() {
cout << "析构函数的调用" << endl;
}
// 调用
void test01() {
// 默认构造函数地调用
// 调用默认构造时最好不要加括号!!!
Person p1;
// 因为编译器会默认为函数的声明, 不会创建对象
Person p2(10);
cout << "p2 -> age :" << p2.age << endl;
// 拷贝构造
Person p3(p2);
cout << "p3 -> age :" << p3.age << endl;
// 显示法构造
Person p11;
Person p12 = Person(10);
Person p13 = Person(p2);
// Person(10) : 匿名对象
// 不要利用拷贝构造函数来匿名调用调用
//Person(p3), 否者会认为对象的声明
// 隐式转化法
Person p4 = 10;
// 拷贝构造
Person p5 = p4;
}
int main() {
test01();
return 0;
}
24、拷贝构造函数的调用时机
#include <iostream>
using namespace std;
class Person {
public:
Person(int age);
Person(const Person& p);
~Person();
int age;
private:
};
Person::Person(int age_) {
age = age_;
cout << "__init__" << endl;
}
Person::Person(const Person& p){
age = p.age;
cout << "__init__ copy" << endl;
};
Person::~Person() {
cout << "__del__" << endl;
}
// 声明
void doWork(Person p);
// 拷贝函数的构造时机
// 使用一个已经创建完对象再来创建另一个对象
void test01() {
Person p1(10);
Person p2(p1);
cout << "p1:" << p1.age << "\tp2:" << p2.age << endl;
}
// 值传递方式给函数参数传值
// 这里会拷贝一个变量p,也就是会再次实例化一个对象
// 使传入的实参p 和 形参p 表面相同, 实际指向的地址是不同的
void test02() {
Person p1(10);
// callback
doWork(p1);
};
void doWork(Person p) {
}
// 值方式返回局部对象
Person doWork02() {
Person p1(10);
cout << (int*)&p1 << endl;
// 返回的是一个拷贝对象, 地址和局部对象的地址不相同
return p1;
}
void test03() {
Person p = doWork02();
cout << (int*)&p << endl;
}
int main() {
//test01();
//test02();
test03();
return 0;
}
25、构造函数的调用规则
#include <iostream>
using namespace std;
// 默认会提供构造函数, 拷贝函数, 析构函数
int main() {
return 0;
}
26、深拷贝与浅拷贝
#include <iostream>
using namespace std;
// 默认会提供构造函数, 拷贝函数, 析构函数
int main() {
return 0;
}
27、初始化列表
#include <iostream>
using namespace std;
class Person {
public:
int a;
int b;
int c;
Person(int x, int y, int z) {
a = x;
b = y;
c = z;
}
};
// 初始化值
class Person1 {
public:
int a;
int b;
/*
Person1() : a(10), b(20) {
}
*/
Person1(int a, int b) : a(a), b(b) {
}
};
int main() {
Person p(1, 2, 3);
cout << "a:" << p.a << "b:" << p.b << "c:" << p.c << endl;
Person1 p1(10, 20);
cout << "a:" << p1.a << "b:" << p1.b << endl;
return 0;
}
28、类对象作为类成员
#include <iostream>
using namespace std;
class Person {
public:
int a;
int b;
int c;
Person(int x, int y, int z) {
a = x;
b = y;
c = z;
}
};
// 初始化值
class Person1 {
public:
int a;
int b;
/*
Person1() : a(10), b(20) {
}
*/
Person1(int a, int b) : a(a), b(b) {
}
};
int main() {
Person p(1, 2, 3);
cout << "a:" << p.a << "b:" << p.b << "c:" << p.c << endl;
Person1 p1(10, 20);
cout << "a:" << p1.a << "b:" << p1.b << endl;
return 0;
}
29、静态成员函数
#include <iostream>
using namespace std;
// 静态成员函数
// 所有对象共享一个函数
// 静态成员函数只能访问静态成员变量
class Person {
public:
// 非静态成员变量
int b;
// 静态成员变量
static int a;
// 静态成员函数
static void func() {
// 静态成员函数可以访问静态成员变量
a = 100; // 静态成员所有类都共享有一份
// b = 200; 错误, 无法区分到底是哪一个对象的
cout << "call func" << endl;
}
private:
static void func2() {
cout << "private static func" << endl;
}
};
// 在类外初始化变量
int Person::a = 0;
void test01() {
// 通过对象进行访问
Person p;
p.func();
// 通过类名进行访问
Person::func();
}
int main() {
test01();
return 0;
}
30、成员变量和成员函数分开储存
#include <iostream>
using namespace std;
// 成员变量 成员函数 分开储存
class Person {
// 非静态成员函数与成员变量分开存储,也就是结果内存空间始终为1字节
// 不属于类的对象上
void func() {
}
// 不属于类的对象上
static void func1() {
}
};
void test01() {
Person p;
// c++ 编译器会给每一个空对象分配一个1字节的内存空间
cout << "size of empty p = " << sizeof(p) << " bit" << endl;
}
class Person2 {
public:
int a;
};
void test02() {
Person2 p2;
// c++ 编译器会给每一个非空对象分配一个4(int)字节的内存空间
cout << "size of non empty p = " << sizeof(p2) << " bit" << endl;
}
class Person3 {
public:
int a;
static int b;
};
int Person3::b = 10;
void test03() {
Person3 p3;
// c++ 编译器会给每一个非空对象分配一个4(int)字节的内存空间, 静态变量不属于类的对象上,不占用空间
cout << "size of non empty p = " << sizeof(p3) << " bit" << endl;
}
int main() {
// test01();
// test02();
//test03();
return 0;
}
31、this指针的用途
#include <iostream>
using namespace std;
class Person {
public:
int age;
// 解决名称冲突
Person(int age) {
// this 指向被调用成员函数所属的对象, this指向p1;
this->age = age;
}
// 返回对象本身
Person& personAddAge(Person p) {
this->age += p.age;
return *this;
}
};
void test01() {
Person p(10);
cout << "p1 -> age : " << p.age << endl;
}
void test02() {
Person p1(10);
Person p2(10);
// 链式编程思想
p2.personAddAge(p1).personAddAge(p1).personAddAge(p1);
cout << "p2 age->" << p2.age << endl;
}
int main() {
//test01();
test02();
return 0;
}
32、空指针访问成员函数
#include <iostream>
using namespace std;
// 空指针调用成员函数
class Person {
public:
int m_age;
void showPersonAge() {
if (this == NULL) {
return;
}
cout << "age = " << this->m_age << endl;
}
void showClassName() {
cout << " this is a class" << endl;
}
};
void test01() {
Person* p = NULL;
// 不会报错
p->showClassName();
// p 并没有创建一个实例,传入的指针为空, this 对应的 p 为空
p->showPersonAge();
}
int main() {
test01();
return 0;
}
33、const修饰成员函数
#include <iostream>
using namespace std;
class Person {
public:
int m_a;
// 即使在常函数中也可以修改这个值, 加入限制 mutable
mutable int m_b;
// 常函数
void showPerson() const
{
// m_a = 100; 不能修改 this—>m_a
// this 指针常量, 指针是不可以修改的; this->NULL 错误, Person* const this;
// this 指向的值一般是可以修改的
// same to =====> const Person* const this; fist const limit the variable
}
};
void test01() {
Person p;
p.showPerson();
}
// 常对象
void test02() {
// 修饰的属性不允许修改
// 可以添加 mutable 使成为可以修改的对象
const Person p;
p.m_b = 100;
// 常对象只能调用常函数
p.showPerson();
// 普通成员函数可能会修改属性与之冲突
}
int main() {
test01();
return 0;
}
34、友元-常函数做友元
#include <iostream>
using namespace std;
// 全局函数做友元
class Buliding {
public:
Buliding() {
mSittingRoom = "客厅";
mBedRoom = "卧室";
}
string mSittingRoom;
private:
// 将全局函数的声明写道这里
friend void goodGay(Buliding* buliding);
string mBedRoom;
};
// gloable function
// 全局函数
void goodGay(Buliding* buliding) {
cout << "好基友的全局函数正在访问 : " << buliding->mSittingRoom << endl;
cout << "好基友的全局函数正在访问 : " << buliding->mBedRoom << endl;
}
void test01() {
Buliding building;
goodGay(&building);
}
int main() {
test01();
return 0;
}
35、友元类
#include <iostream>
using namespace std;
class Buliding {
public:
Buliding();
string mSittingRoom;
private:
string mBedRoom;
// 写在这里-------->
friend class GoodGay;
};
Buliding::Buliding() {
mSittingRoom = "客厅";
mBedRoom = "卧室";
}
class GoodGay {
public:
GoodGay();
Buliding* building;
void visit();
};
GoodGay::GoodGay() {
// 创建建筑物对象
building = new Buliding;
}
void GoodGay::visit() {
cout << "好基友的类正在访问: " << building->mSittingRoom << endl;
cout << "好基友的类正在访问: " << building->mBedRoom << endl;
}
int main() {
GoodGay goodGay;
goodGay.visit();
return 0;
}
36、成员函数做友元
#include <iostream>
using namespace std;
class GoodGay;
class Building;
// GoodGay 一定要先放在 Building 前面, Building创建朋友时,一定要先存在 visit()在类GoodGay中
class GoodGay {
public:
GoodGay();
Building* building;
void visit1(); // 让visit1 可以访问building中的私有成员
void visit2(); // 让visit2 可以访问building中的私有成员
private:
};
class Building {
friend void GoodGay::visit1();
public:
string mSettingRoom;
Building();
private:
string mBadRoom;
};
Building::Building() {
mSettingRoom = "客厅";
mBadRoom = "卧室";
}
GoodGay::GoodGay() {
building = new Building;
}
void GoodGay::visit1() {
cout << "visit1正在访问:" << building->mSettingRoom << endl;
cout << "visit1正在访问:" << building->mBadRoom << endl;
}
void GoodGay::visit2() {
cout << "visit2正在访问:" << building->mSettingRoom << endl;
}
void test01() {
GoodGay gg;
gg.visit1();
gg.visit2();
}
int main() {
test01();
return 0;
}
37、加号运算符重载
#include <iostream>
using namespace std;
// 加号运算符重载
class Person {
public:
int a;
int b;
Person() {
}
Person(int a_, int b_) {
a = a_;
b = b_;
}
// 函数的内部进行重载
Person operator+(Person& p) {
Person temp;
temp.a = this->a + p.a;
temp.b = this->b + p.b;
return temp;
}
private:
};
// 函数外部的重载
/*
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.a = p1.a + p2.a;
temp.b = p1.b + p2.b;
return temp;
}
*/
// 成员函数的重载
void test01() {
Person p1(10, 10);
Person p2(10, 10);
Person p3 = p1 + p2;
cout << p3.a << " " << p3.b << endl;
}
int main() {
test01();
return 0;
}
38、左移运算符重载
#include <iostream>
using namespace std;
// 可以输出自定义的数据类型
class Person {
friend ostream& operator<<(ostream& cou, Person& p);
friend void test01();
int a;
int b;
};
// 本质 cout << p ---> operator<<(cout, p)
// cout 全局只能有一个, 不能出现两个不同的地址
// 返回值是为了添加链式语法
// 引用的本身是起别名, out 与 cout 指向的是同一个地址
ostream& operator<<(ostream& cou, Person& p) {
cou << "m_a = " << p.a;
return cou;
}
void test01() {
Person p;
p.a = 10;
p.b = 10;
cout << p << " added" << endl;
}
int main() {
test01();
return 0;
}
39、递增运算符重载
#include <iostream>
using namespace std;
class MyPlus {
friend ostream& operator<<(ostream& cout, MyPlus& myplus);
friend void test01();
public:
MyPlus() {
a = 0;
}
// ++ 前置重载
MyPlus& operator++() {
++a;
return *this;
}
// 后置重载
// 如果返回的是引用那么返回的是局部对象的引用,所以返回的是一个值
MyPlus& operator++(int) {
// 记录当时结果
// MyPlus temp = *this;
// 递增
a++;
// 将记录的结果返回
return *this;
}
private:
int a;
};
ostream& operator<<(ostream& cout, MyPlus& myplus) {
cout << myplus.a;
return cout;
}
void test01() {
MyPlus myplus;
cout << ++(++myplus) << endl;
myplus++;
myplus++;
cout << myplus++ << endl;
}
int main() {
test01();
return 0;
}
40、赋值运算符重载
#include <iostream>
using namespace std;
// 赋值运算符的重载
class Person {
public:
int* age;
Person(int age) {
this->age = new int(age);
};
~Person() {
if (age != NULL) {
delete this->age;
}
}
Person& operator=(Person& p) {
// 默认 age = p.age
// 将拷贝出来的这一份改变age指针的指向
if (age != NULL) {
delete age;
age = NULL;
}
age = new int(*p.age);
return *this;
}
};
void test01() {
Person p1(18);
cout << "person1 age: " << *p1.age << endl;
Person p2(20);
Person p3(30);
// 赋值操作, 浅拷贝,导致堆区的内存重复释放
p2 = p1 = p3;
cout << "person2 age: " << *p2.age << endl;
}
int main() {
test01();
return 0;
}
41、关系运算符重载
#include <iostream>
using namespace std;
class Person {
public:
string name;
int age;
Person(string name_, int age_) : name(name_), age(age_) {
}
bool operator==(Person& p) {
if (this->age == p.age && this->name == p.name) {
return true;
}
else {
return false;
}
}
};
void test01() {
Person p1("tom", 18);
Person p2("tom", 18);
if (p1 == p2) {
cout << "p1 == p2" << endl;
}
else {
cout << "p1 != p2" << endl;
}
}
int main() {
test01();
return 0;
}
42、函数调用运算符重载
#include <iostream>
using namespace std;
// 函数调用运算符重载
class Print {
public:
// 重载函数调用运算符
// python "__call__"
void operator()(string test) {
cout << test << endl;
}
};
void print02(string s) {
cout << s << endl;
}
void test01() {
Print print;
print("Hello World");
print02("hello world2");
// 匿名对象, 执行完后就释放内存空间
Print()("lambda ");
}
int main() {
test01();
return 0;
}
43、继承的基本语法
#include <iostream>
using namespace std;
class Header {
public:
void header() {
cout << "public header" << endl;
}
};
// 继承的写法
class Java : public Header {
public:
void java() {
cout << "java" << endl;
}
};
void test01() {
Header head;
head.header();
Java java;
java.header();
java.java();
}
int main() {
test01();
return 0;
}
44、继承方式
#include <iostream>
using namespace std;
// public继承, 保持父类中public和protected
// protected继承, 将父类中的public与protected全部变为子类中的protected
// private 继承, 将父类中的public与protected全部变为子类中的private
int main() {
return 0;
}
45、继承中的对象模型
#include <iostream>
using namespace std;
// 继承中的对象模型
class Base {
public:
int a;
private:
int b;
protected:
int c;
};
class Son : public Base {
public:
int d;
};
void test01() {
Son son;
// 父类中所有的属性都会被继承, 只是被编译器屏蔽了,因此访问不到
cout << sizeof(son) << endl; // 16 bites
}
int main() {
test01();
return 0;
}
46、继承中的构造与析构的顺序
#include <iostream>
using namespace std;
class Father {
public:
Father();
~Father();
private:
};
Father::Father() {
cout << "father __init__" << endl;
}
Father::~Father() {
cout << "father __del__" << endl;
}
class Son: public Father {
public:
Son();
~Son();
private:
};
Son::Son() {
cout << "son __init__" << endl;
}
Son::~Son() {
cout << "son__del__" << endl;
}
int main() {
Son son;
return 0;
}
47、同名成员处理
#include <iostream>
using namespace std;
class Father {
public:
int a = 10;
void func() {
cout << "father func" << endl;
}
void func(int) {
cout << "refunc in father" << endl;
}
};
class Son : public Father {
public:
int a = 100;
void func() {
cout << "son func" << endl;
}
};
int main() {
Son son;
// 访问儿子中的属性
cout << son.a << endl;
son.func();
// 访问父亲中的属性
cout << son.Father::a << endl;
son.Father::func();
// 访问父亲中的重构函数
son.Father::func(3);
return 0;
}
48、同名静态成员的处理
#include <iostream>
using namespace std;
class Base {
public:
static int a;
static void func() {
cout << "father static func" << endl;
}
static int func(int a_, int b_) {
int c = a_ + b_;
return c;
}
};
int Base::a = 10;
class Son : public Base{
public:
static int a;
static void func() {
cout << "son static func" << endl;
}
};
int Son::a = 100;
// 同名静态成员的属性
void test01() {
Son s;
// 通过对象进行访问
cout << "a= " << s.a << endl;
cout << "base a=" << s.Base::a << endl;
// 通过类名进行访问
// first :: -> 通过类名的方式访问, seconed :: -> 代表访问父类作用域下的属性
cout << Son::Base::a << endl;
cout << Son::a << endl;
// 通过对象访问
s.func();
s.Base::func();
// 通过类名的方式进行访问
Son::func();
Son::Base::func();
cout << "sum from father static method: " << Son::Base::func(10, 15) << endl;
}
int main() {
test01();
return 0;
}
49、多继承语法
#include <iostream>
using namespace std;
class Base1 {
public:
int a = 10;
};
class Base2 {
public:
int a = 20;
};
class Son : public Base1, public Base2 {
public:
int c = 30;
int d = 40;
};
void test01() {
Son son;
cout << "sizeof son = : " << sizeof(son) << endl;
// 当父类中出现同名的成员时需要添加作用域进行区分
cout << "son -> base1 -> prop:a ->" << son.Base1::a << endl;
}
int main() {
test01();
return 0;
}
50、菱形继承or钻石继承
#include <iostream>
using namespace std;
class Animal {
public:
int age;
};
// 利用虚继承,解决菱形继承的问题 --> virtual -> 最大的类叫做虚基类
// 此时的数据为共享数据
// vbptr : virtual base poninter --> 虚基类指针 -->vbtable: virtual base table --> 虚基类表格
// 第一个 vbptr + 8( 地址偏移量) = 第二个vbptr + 4(地址偏移量) ---> vbtable(储存age的表格)
// 虚继承的不是两个数据,而是继承的是两个指针,指向同一个地址
class Sheep: virtual public Animal {
};
class Cat : virtual public Animal {
};
class SheepCat : public Sheep, public Cat {
};
void test01() {
SheepCat sc;
sc.Sheep::age = 18;
sc.Cat::age = 19;
cout << "sheep age: " << sc.Sheep::age << endl;
cout << "cat age: " << sc.Cat::age << endl;
// 菱形继承导致数据有两份,导致资源浪费
cout << "虚继承之后: " << sc.age << endl;
}
int main() {
test01();
return 0;
}
51、多态的基本语法
#include <iostream>
using namespace std;
class Animal {
public:
// ------------ 动态多态 -------------- 地址的晚绑定:运行阶段确定函数 ------------
// 加了 virtual 后, 会优先从继承的类中寻找函数, 找不到再从父类中找
// 这个就是地址的晚绑定
// 否则会优先从父类中寻找,这个就是地址的早绑定
virtual void speak() {
cout << "animal speak " << endl;
}
};
class Cat : public Animal{
public:
void speak() {
cout << "cat speak " << endl;
}
};
// 执行说话的函数
// Animla & animal = cat;
// 运行相互引用 ---》 animal中的speak函数
// 地址在编译阶段就确定了函数的地址
void doSpeak(Animal& animal) {
animal.speak();
}
// 如果执行时想让猫说话,就不能进行早绑定
class CatVir : public Animal {
public:
// 虚函数实现地址晚绑定
void speak() {
cout << "cat speak " << endl;
}
};
void test01() {
cout << "地址早绑定" << endl;
Cat cat;
doSpeak(cat);
cout << "addr 晚绑定" << endl;
CatVir catvir;
doSpeak(catvir);
}
int main() {
test01();
return 0;
}
52、动态多态的原理剖析
#include <iostream>
using namespace std;
class AnimalOrg {
public:
// ------------ 动态多态 -------------- 地址的晚绑定:运行阶段确定函数 ------------
// 加了 virtual 后, 会优先从继承的类中寻找函数, 找不到再从父类中找
// 这个就是地址的晚绑定
// 否则会优先从父类中寻找,这个就是地址的早绑定
void speak() {
cout << "animal speak " << endl;
}
};
class AnimalChg {
public:
// vfptr: 虚函数指针(虚函数表指针
// vitual function pointer
// AnimalChg::vftable 内部会记录虚函数的地址 ,
virtual void speak() {
cout << "animal speak " << endl;
}
};
class Cat : public AnimalOrg {
public:
// 当子类重写了父类中的虚函数, 父类中的虚函数表会将父类中的虚函数表替换为子类的虚函数
// 从而确定函数的入口地址
//只是提换子类中的虚函数表
// animla& animal = cat -->会从cat的虚函数表中找cat的speak函数
void speak() {
cout << "cat speak " << endl;
}
};
void doSpeak(AnimalOrg& animal) {
animal.speak();
}
void test01() {
cout << "sizeof animlaOrg = " << sizeof(AnimalOrg) << endl; // 1 bits
cout << "sizeof animalChg = " << sizeof(AnimalChg) << endl; // 8 bits 指针 和电脑有关 32位->4 bits
}
int main() {
test01();
return 0;
}
53、多态案例:计算机类
#include <iostream>
using namespace std;
class Calculator {
public:
int num1;
int num2;
int getResult(string oper) {
if (oper == "+") {
return num1 + num2;
}
else if (oper == "-") {
return num1 - num2;
}
else if (oper == "*") {
return num1 * num2;
}
// 如果需要添加新的功能,需要修改源码
// 对扩展进行开放,对修改进行关闭
}
};
void test01() {
Calculator c;
c.num1 = 10;
c.num2 = 9;
cout << c.num1 << "+" << c.num2 << "=" << c.getResult("+") << endl;
cout << c.num1 << "-" << c.num2 << "=" << c.getResult("-") << endl;
cout << c.num1 << "*" << c.num2 << "=" << c.getResult("*") << endl;
}
// 利用多态来写计算器
class AbstructCalc {
public:
int num1;
int num2;
virtual int getResult() {
return 0;
}
};
class Add : public AbstructCalc {
public:
int getResult() {
return num1 + num2;
}
};
class Sub : public AbstructCalc {
public:
int getResult() {
return num1 - num2;
}
};
void test02() {
// 父类的指针指向子类的对象
AbstructCalc* abc = new Add;
abc->num1 = 10;
abc->num2 = 19;
cout << abc->num1 << "+" << abc->num2 << "=" << abc->getResult() << endl;
// 用完后记得销毁
delete abc;
abc = NULL;
AbstructCalc* abc1 = new Sub;
abc1->num1 = 10;
abc1->num2 = 19;
cout << abc1->num1 << "-" << abc1->num2 << "=" << abc1->getResult() << endl;
// 用完后记得销毁
delete abc;
}
int main() {
test02();
return 0;
}
54、纯虚函数和抽象类
#include <iostream>
using namespace std;
// 抽象类
class Base {
public:
// 纯虚函数
// 无法实例化对象, 只能被继承
virtual void func() = 0;
};
class Son : public Base {
public:
void func() {
cout << "rewrite func from father" << endl;
}
};
int main() {
Son s;
s.func();
// 多态写法
Base* p = new Son;
p->func();
return 0;
}
55、多态案例:制作饮品
#include <iostream>
using namespace std;
// 多态的案例二,制作饮品
class AbstructDrinking {
public:
virtual void Boild() = 0;
virtual void Brew() = 0;
virtual void PourCup() = 0;
virtual void PutSomething() = 0;
// 制作饮品
void makeDrink() {
Boild();
Brew();
PourCup();
PutSomething();
}
};
class Coffee : public AbstructDrinking {
public:
void Boild() {
cout << "煮水" << endl;
}
void Brew() {
cout << "冲泡茶叶" << endl;
}
void PourCup() {
cout << "倒入茶杯" << endl;
}
void PutSomething() {
cout << "加入枸杞" << endl;
}
};
void test01() {
AbstructDrinking* abs = new Coffee;
abs->makeDrink();
delete abs;
}
int main() {
test01();
return 0;
}
56、虚析构和纯析构
#include <iostream>
using namespace std;
class Animal {
public:
Animal() {
cout << "animal __init__" << endl;
}
// 将父类的析构函数变为虚析构函数 父类指针释放类对象不干净的问题 -------------> 虚析构:有声明有实现
/*
virtual ~Animal() {
cout << "animal __del__" << endl;
}
*/
// -------------------------------------------纯析构---------------需要代码实现,只有声明
// 纯析构之后这个类也属于抽象类
virtual ~Animal() = 0;
// 虚函数
virtual void speak() = 0;
};
// 虚构的实现
Animal::~Animal() {
cout << "Animal__del__" << endl;
}
class Cat : public Animal {
public:
string* name;
Cat(string name) {
cout << "cat __init__" << endl;
this->name = new string(name);
}
~Cat() {
if (name != NULL) {
// delete father pointer 不会调用这一行,但是可以利用虚析构来解决这个问题
cout << "cat __del__" << endl;
delete name;
}
}
void speak() {
cout << *name << "小猫在说话" << endl;
}
};
void test01() {
Animal* animal = new Cat("tom");
animal->speak();
delete animal;
}
int main() {
test01();
return 0;
}
57、实例:电脑组装
#include <iostream>
using namespace std;
class Cpu {
public:
virtual void calculate() = 0;
};
class VideoCard {
public:
virtual void display() = 0;
};
class Memory {
public:
virtual void storage() = 0;
};
class Computer {
public:
Computer(Cpu* cpu_, VideoCard* vediocard_, Memory* memory_) {
cpu = cpu_;
videoCard = vediocard_;
memory = memory_;
}
void work() {
cpu->calculate();
videoCard->display();
memory->storage();
}
~Computer() {
if (cpu != NULL && videoCard != NULL && memory != NULL) {
delete cpu;
delete videoCard;
delete memory;
}
}
private:
Cpu* cpu;
VideoCard* videoCard;
Memory* memory;
};
// 具体厂商
class InterCpu : public Cpu {
public:
virtual void calculate() {
cout << "inter cpu work !" << endl;
}
};
class InterVideoCard : public VideoCard {
public:
virtual void display() {
cout << "inter videoCard work !" << endl;
}
};
class InterMemory : public Memory {
public:
virtual void storage() {
cout << "inter Memory work !" << endl;
}
};
void test() {
Cpu* cpu = new InterCpu;
VideoCard* ved = new InterVideoCard;
Memory* memory = new InterMemory;
Computer* computer = new Computer(cpu, ved, memory);
computer->work();
delete computer;
cpu = NULL;
ved = NULL;
memory = NULL;
}
int main() {
test();
return 0;
}
58、文件操作:写文件
#include<iostream>
using namespace std;
#include<fstream>
// 文本文件中的写文件
void test01() {
// 创建流对象
// output file stream
ofstream ofs;
// 指定打开的方式, ios(input-output-stream)
ofs.open("test61.txt", ios::out);
// 写内容
ofs << "name:alex\nsex:female\nage:18" << endl;
ofs.close();
}
int main() {
test01();
return 0;
}
59、文件操作:读文件
#include <iostream>
using namespace std;
#include<fstream>
#include<string>
void test01() {
// 创建文件流对象
ifstream ifs;
// 打开文件
ifs.open("test61.txt", ios::in);
// 判断是否打开
if (!ifs.is_open()) {
cout << "文件打开失败了" << endl;
return;
}
// 读文件
// 第一种-----每一次写入都会覆盖上一次的写入
/*
char buf[1024] = { 0 };
while (ifs >> buf) {
cout << buf << endl;
}
cout << buf << "========" << endl;
ifs.close();*/
// 第二种
/*
char buf[1024] = { 0 };
while (ifs.getline(buf, sizeof(buf))) {
cout << buf << endl;
;
};
*/
// 第三种
string buf;
// 包含头文件 string
/*
while (getline(ifs, buf)) {
cout << buf << endl;
}
ifs.close();
*/
// 第四种, EOF == end of line
char c;
while ((c = ifs.get()) != EOF) {
cout << c;
}
ifs.close();
}
int main() {
test01();
return 0;
}
60、二进制写文件
#include <iostream>
using namespace std;
#include<fstream>
// 二进制写文件
struct Person {
char name[64];
int age;
};
void test01() {
ofstream ofs("binary63.txt", ios::out | ios::binary);
Person p = { "张三", 18 };
ofs.write((const char*)&p, sizeof(p));
ofs.close();
}
int main() {
test01();
return 0;
}
61、二进制读文件
#include <iostream>
using namespace std;
#include<fstream>
struct Person {
char name[64];
int age;
};
// 二进制文件写入的是数据的指针,读文件时也是用的一个指针来储存读出来的指针内容
// 二进制的文件读写用的指针都必须要强制转化为 char 类型的指针变量
// 二进制写文件时用的是 常量指针
// 二进制读文件时用的就是普通的指针
void test01() {
ifstream ifs;
ifs.open("binary63.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "error !" << endl;
return;
}
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << p.name << "\t" << p.age << endl;
}
int main() {
test01();
return 0;
}