基础数据类型

char

只能表示一个字符, 例如65表示A

#include <iostream>
#include "string"
using namespace std;

int main() {
    char letter;
    letter = 65;
    cout << "letter: " << letter << endl;
    // 输出: A
}

指针

类型声明 * 变量名;
int * p;
#include <iostream>
#include "main.h"
using namespace std;

int main() {
    func();
    return 0;
}

void func(){
    int * p;
    int a = 10;
    p = &a;
    cout << "a的地址: " << &a << endl;
    cout << "p内容: " << p << endl;
    
    cout << "p的地址: " << &p << endl;
    cout << "p中a的内容: " << *p << endl;
}

指针所占空间

32位系统下占4个字节(不管什么类型);64位占8个字节;

空指针、野指针

前者:指针声明在0~255之间为系统所用,使用会报错;

后者:使用了自定义的内存地址(不存在的地址),报错

指针常量、常量指针

void func() {
    // 常量指针,指针的指向可以改,指针的值不可以改
    int a = 10;
    int b = 20;
    const int *p = &a;
    p = &b;
    cout << ": " << *p << endl;

    //指针常量,指针值可以改,指向不可以改
    int * const c = &b;
    * c = 10;

    // const既修饰指针,又修饰常量,两者都不可以改
    const int * const p3 = &a;
}

指针和数组

如果指针指向的是一个数组,那么*p就是数组的第一位

void func() {
    int ls[] = {1, 2, 3, 4, 5};
    int *p = ls;
    for (int i = 1; i < 5; i++) {
        cout << *p << endl;
        p++;
    }
}

指针和函数

主要是演示,函数如何实现修改实参的值;

void func(int *p1, int *p2);

int main() {
    int a = 10;
    int b = 20;
    func(&a, &b);
    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
    return 0;
}

void func(int *p1, int *p2) {
    int tem = *p1; // *p1 解引用,p1指向的内存地址,转换成实际的值。
    *p1 = *p2;
    *p2 = tem;
}

指针、数组、函数、

int main() {
    int ls[] = {1, 2, 3};
    int len = sizeof(ls) / sizeof(ls[0]);
    cout << "len:" << len << endl;
    return 0;
}

结构体类型

声明结构体变量时,不可以省略struct 关键字。

创建结构体变量时,可以省略struct关键字,

struct Func {
    string name;
    int age;
    int hight;
};

int main() {
    //第一种方式
    Func s1;
    s1.name = "张三";
    s1.age = 10;
    s1.hight = 12;
    cout << "s1 Name: " << s1.name << "s1.age: " << s1.age << "s1.hgint: " << s1.hight << endl;
	//第二种方式
    Func s2 = {"李四", 12, 17};
    cout << "s2 Name: " << s2.name << "s2.age: " << s2.age << "s2.hgint: " << s2.hight << endl;

    return 0;
}

结构体数组

#include <iostream>
#include "main.h"
#include "string"

using namespace std;

struct Func {
    string name;
    int age;
    int hight;
};

int main() {
    Func s1[] = {
            {"张三:", 80, 60},
            {"李四",  18, 99}
    };
    s1[1].name = "李四2";
    s1[1].age = 12;
    s1[1].hight = 18;
    int len = sizeof(s1) / sizeof(s1[0]);
    for (int i = 0; i < len; i++) {
        cout << "name: " << s1[i].name
        << "age: " << s1[i].age
        << "hight: "<< s1[i].hight << endl;
    }
    return 0;
}

**结构体指针 **

声明:结构体变量 * 指针变量
输出结构体指针中的内容:结构体指针->参数关键字
#include <iostream>
#include "main.h"
#include "string"

using namespace std;

struct Func {
    string name;
    int age;
    int hight;
};

int main() {
    Func s1 = {"Mir Li", 12, 18};
    Func *p = &s1;
    cout << "name: " << p->name << "age: " << p->age << "hight: " << p->hight << endl;
    return 0;
}

shared_ptr指针

在c++11版本中推出的智能指针,目的是为了解决指针的创建和销毁的管理问题,它负责自动释放所指的对象。shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。

引用

引用的本质是指针常量;即:引用的值可以修改,但引用的指向不可以修改;

void func(const int& a)
{
    // 加入过const 修饰后的引用,在局部函数中不允许被修改。
    //a = 10;
    int c = 100;
    cout << "a: " << a << endl;
}

int main()
{
    int main_a = 100;
    func(main_a);
    return 0;
}

函数

~虚函数

用来解决行参中,父类指针指向子类实例时,强制转换,导致实例化的子类,使用的是父类的成员函数,而不是自己内部定义的成员函数的问题。

  • 地址早绑定: 在编译阶段就确定了函数地址;

  • 地址晚绑定: 使用virtual 修饰成员函数, 使参数在执行阶段确定地址

虚函数是动态多态的实现方式;多态即是,接口一样,参数一样,但是实现出来的效果不一样。

动态多态满足条件:

  1. 有继承关系
  2. 子类重写父类的虚函数
#include "string"
#include <iostream>
#include "string"
using namespace std;

class A {
public:
	int age = 10;
	virtual void speak() {
		cout << "A 说话" << endl;
	};
};

class B: public A {
public:
	virtual void speak() {
		cout << "B 说话" << endl;
	};
};

void func(A & a) {
	a.speak();
};

int main() {
	B b;
	func(b);
	return 1;
}

纯虚函数和抽象类

纯虚函数是实现抽象类的方式

父类中只要有一个纯虚函数, 改类就被称为抽象类, 抽象类的特点: 1、无法实例化对象。2、抽象类的子类,必须要重写父类。

语法: virtul 返回值类型 函数名 (参数列表) = 0;

#include "string"
#include <iostream>
#include "string"
using namespace std;

class A {
public:
	virtual void speak() = 0;
};

class B: public A {
public:
    // 子类必须重写父类中的纯虚函数,否则无法实例化
	void speak() {
		cout << "B 说话" << endl;
	};
};

int main() {
	B b;
	b.speak();
	return 1;
}

虚析构和纯虚析构

目的: 父类写虚析构的目的是为了解决,继承关系中,实例化的子类,无法正常调用到自己的析构函数,一般不需要这么做。

注意点:

  • 虚析构和纯虚析构,同时只能有一个存在,纯虚函数的目的是为了让子类必须重写这个函数,否则就报错。

  • 析构函数的调用时机是在实例销毁时触发,即delete 实例 时才会触发析构函数。

  • 即使父类的析构函数为纯虚析构,也要在全局做析构函数声明,否则会报错,原因是,父类也会有自己的堆栈数据要释放。

#include "string"
#include <iostream>
#include "string"
using namespace std;


class Animal {
public:
	string * name1;
	Animal() {
		cout << "Animan执行了构造函数" << endl;
	};
	virtual ~Animal() {
		cout << "Animan执行了析构函数" << endl;
	};
    // 纯虚析构,但是也要在全局做实现。
    //virtual ~Animal() = 0 ;
	virtual void func() = 0;
};

//Animal::~Animal() {
//	cout << "Animan执行了析构函数" << endl;
//};

class Cat:public Animal {
public:
	Cat(string name) {
		this->name = new string(name);
		cout << "Cat执行了构造函数" << endl;
	};
	~Cat() {
		cout << "Cat执行了析构函数" << endl;
		if (name != NULL) {
			delete name;
			name = NULL;
		}
	};
	void func() {
		cout << "name 是:" << name << endl;
	};
	string* name;
};

int main() {
	// 如果此时Animal中的func不是虚函数,则会调用Animal的func,原因是地址早绑定的关系。
	Animal* ani = new Cat("tim");
	ani->func();
	delete ani;
	return 1;
}

~默认值

如果函数声明的时候有默认值,那么函数实现的时候,就不能再给参数声明默认值,反之同理。

#include <iostream>
#include "string"
using namespace std;

int  func(int a = 10, int b = 20);

int main()
{
    cout << "c: " << func(11, 22);
    return 0;
}

int  func(int a, int b)
{
    return a + b;
}

~站位参数


#include <iostream>
#include "string"
using namespace std;

int func(int a, int);

int main()
{
    cout << "c: " << func(11, 22);
    return 0;
}


int func(int a, int = 20)
{
    return a;
}

~重载

函数重载的目的是为了提高代码的复用性。

前提:

  • 在同一个作用域
  • 函数名相同
  • 函数参数类型不同 或者 参数个数不同 或者 顺序不同

返回值不能作为重载的条件。

#include <iostream>
#include "string"
using namespace std;

int func(int a, int = 20)
{
    cout << "func(int a, int = 20) " << endl;
    return a;
}

int func(float a, double = 20)
{
    cout << "func(float a, double = 20) " << endl;
    return a;
}

int main()
{
    func(11, 2);
    return 0;
}

~重载的坑

以下均是错误示范,应避免情况出现

  • 引用作为重载条件

  • 重载遇到默认值参数

会出现二义性问题

#include <iostream>
#include "string"
using namespace std;

int func(int a,int b = 10)
{
    cout << "func(int a, int b) " << endl;
    return a;
}

int func(int a)
{
    cout << "func(int a) " << endl;
    return a;
}

int main()
{
    func(11);
    return 0;
}

struct和class的区别

struct默认防控属性是public(公共的),而class默认的防控属性是private(私有的)

struct A{};
class B : A {};	//如果不写继承类型,默认为private继承
struct C : B{};	//默认为public继承

C++ 编译器会给类添加4个函数

image-20201124222942936

权限

  • public 公共权限
  • protected 保护权限 ,类内可以访问,类外不可以访问
  • private 私有权限 ,类内可以访问,类外不可以访问
#include <iostream>
#include "string"
using namespace std;

class Peo
{
public:
	string name;
	void func()
	{
		cout << "name: " << name << endl;
		cout << "m_car: " << m_car << endl;
		cout << "m_pwd: " << m_pwd << endl;
		cout << "11111: " << 1111111 << endl;
		name = "李四";
		m_car = "奔驰";
		m_pwd = "123";
		cout << "name: " << name << endl;
		cout << "m_car: " << m_car << endl;
		cout << "m_pwd: " << m_pwd << endl;
	};

	//保护权限
protected:
	string m_car;
private:
	string m_pwd;
};


int main()
{
	Peo p1;
	p1.func();
	return 1;
}

构造函数

构造函数: 初始化阶段,在实例化之前执行的代码段。

析构函数: 销毁阶段,最后执行的代码段,一般用来 释放堆区内存。

#include <iostream>
#include "string"
using namespace std;

class Peo
{
public:
	string name;
	Peo() {
		cout << "构造函数执行了。。。" << endl;
	};
	~Peo() {
		cout << "析构。。。" << endl;
	}
};


int main()
{
	Peo p1;

}

调用

不能使用匿名的方式,调用构造函数,比如: Peo p1();, 这种错误写法,编译器会认为是函数的声明

#include <iostream>
#include "string"
using namespace std;

class Peo
{
public:
	string name;
	Peo(int a) {
		cout << "构造函数执行了。。。:" << a << endl;
	};

	~Peo() {
		cout << "析构。。。" << endl;
	}
};


int main()
{
	// 1.括号法,常用
	Peo p1(10);

	// 2 显示法
	//Peo p2 = Peo(10);
	
	// 3 隐式转换法
	//Peo p3 = 10;

}

成员函数

常函数:

  • 成员函数后加const是常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加mutable后,在场函数中就可修改了
#include <iostream>
#include "string"
using namespace std;

struct Student {
public:
	mutable int a = 10;
	int b = 10;
	// 常函数
	void func() const {
		a = 11;
		cout << "常函数:" << a << endl;
	}
};

void test() {
	Student p1;
	const Student p2;
	p1.func();
	p2.a = 11;
	//p2.b = 111; 常函数无法修改普通成员变量

}

int main() {
	test();
	return 0;
}

常对象:

  • 声明对象前加const为常对象
  • 常对象只能调用常函数,和mutable变量

struct和class 声明的区别

前者默认的权限级别是公共的,后者默认是私有的。

#include <iostream>
#include "string"
using namespace std;

class Peo
{
	string name;
};

struct Peo2 {
	string name;
};

int main()
{
	Peo p1;
	Peo2 p2;
	p1.name = "111";
	p2.name = "123";
	return 1;
}

友元

关键字: friend

三种实现:

  • 全局函数做友元
  • 类做友元: 一个类可以访问另一个类的私有成员
  • 成员函数做又元

全局函数做又元

#include <iostream>
#include "string"
using namespace std;

struct Student {
	friend void test(Student* p1);
public:
	string home = "卧室";
private:
	string car = "奔驰";
};

void test(Student * p1) {
	cout << "p1->home: " << p1->home << endl;
	cout << "p1->car: " << p1->car << endl;
}

int main() {
	Student p1;
	test(&p1);
	return 0;
}

类做友元

#include <iostream>
#include "string"
using namespace std;

class Student {
	friend class Teacher;
public:
	Student();
	string home;
private:
	string car;
};

Student::Student()
{
	car = "奔驰";
	home = "卧室";
};

class Teacher {
public:
	Student* p;
	Teacher();
	~Teacher();
	void test();
};

// 类外写成员函数,前提要在类中的public中做声明即可.
Teacher::Teacher()
{
	p = new Student;
};
Teacher::~Teacher()
{
	cout << "正在释放堆栈数据" << endl;
	delete p;
}

void Teacher::test() {
	cout << "类元访问car: " << p->car << endl;
	cout << "类元访问home: " << p->home << endl;
}

int main() {
	Teacher t1;
	t1.test();
	return 0;
}

成员函数做又元

待调整

#include <iostream>
#include "string"
using namespace std;

class Teacher;
class Student {
	friend void Teacher::test();
public:
	Student() {
		car = "奔驰";
		home = "卧室";
	};
	string home;
private:
	string car;
};


class Teacher {
public:
	Teacher() {
		p = new Student;
	};
	void test() {
		cout << "类元访问car: " << p->car << endl;
		cout << "类元访问home: " << p->home << endl;
	};
private:
	Student * p;
};

int main() {
	Teacher t1;
	t1.test();
	return 0;
}

正确内容

#include<iostream>
#include<string>
using namespace std;

class Building;
class goodGay
{
public:
	goodGay();
	void visit1();
	void visit2();

private:
	Building* building;
};

class Building
{
	friend void goodGay::visit1();
public:
	Building();
public:
	string m_sittingroom;	//客厅
private:
	string m_bedroom;		//卧室
};

Building::Building()
{
	this->m_bedroom = "卧室";
	this->m_sittingroom = "客厅";
}

goodGay::goodGay()
{
	building = new Building;
}
void goodGay::visit1()
{
	cout << "基友正在" << this->building->m_sittingroom << endl;
	cout << "基友正在" << this->building->m_bedroom << endl;

}
void goodGay::visit2()
{
	cout << "基友正在" << this->building->m_sittingroom << endl;
	//cout << "基友正在" << this->building->m_bedroom << endl;
}
void test01()
{
	goodGay gg;
	gg.visit1();
	gg.visit2();
}

int main()
{
	test01();

	system("pause");
	return EXIT_SUCCESS;
}

运算符重载

  • 全局函数重载

  • 成员函数重载

加号运算符重载--成员函数

#include<iostream>
#include<string>
using namespace std;


class Student {
public:
	int a = 10;
	int b = 20;
	Student operator+(Student& s1) {
		Student stu;
		stu.a = s1.a + this->a;
		stu.b = s1.b + this->b;
		return stu;
	}
};

void test01()
{
	Student p1;
	p1.a = 1;
	p1.b = 2;

	Student p2;
	p2.a = 11;
	p2.b = 22;

	Student p3 = p1 + p2;
    //成员本质重载
    //Student p3 = p1.operator+(p2);
	cout << "p3: " << p3.a << endl;
	cout << "p3: " << p3.b << endl;
}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

加号运算符重载--全局函数重载

#include<iostream>
#include<string>
using namespace std;


class Student {
public:
	int a = 10;
	int b = 20;

};

Student operator+(Student& s1, Student& s2) {
	Student stu;
	stu.a = s1.a + s2.a;
	stu.b = s1.b + s2.b;
	return stu;
}
void test01()
{
	Student p1;
	p1.a = 1;
	p1.b = 2;

	Student p2;
	p2.a = 11;
	p2.b = 22;

    Student p3 = p1 + p2;
    //全局函数本质重载(全写)
	//Student p3 = operator+(p1, p2);
    
	cout << "p3: " << p3.a << endl;
	cout << "p3: " << p3.b << endl;
}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

左移运算符重载

只有全局模式的左移运算符重载, 成员左移运算符重载会将cout放置在<< 的右侧

#include<iostream>
#include<string>
using namespace std;

class Student {
    //配合友元,实现访问私有内容.
	friend ostream &operator<<(ostream &out, Student &p);
public:
	Student(int a, int b) {
		this->a = a;
		this->b = b;
	};
private:
	int a;
	int b;
};

ostream &operator<<(ostream &out, Student& p) {
	out << "m_A: " << p.a << "   m_B:"<< p.b << endl;
	return out;
}

void test01()
{
	Student s1(10, 20);
	cout << s1;
}

int main()
{
	test01();
	system("pause");
	return 1;
}

递增/递减运算符

#include<iostream>
#include<string>
using namespace std;

class Student {
	friend ostream& operator<<(ostream& out, Student p1);
public:
	Student() {
		a = 0;
	};
	// 重置前置运算符. 一定要返回引用 & , 原因是如果不返回引用,返回的是个新对象,那么无法在对原值进行累加
	Student& operator++() {
		++a;
		cout << "前缀++a: " << a << endl;
		return *this;
	};
	// 重载后置运算符. 后置运算符不能返回引用,原因是局部对象的引用会在当前函数执行完毕后,被自动释放,如何使用引用,会出现错误
	Student operator++(int) {
		Student s1 = *this;
		a++;
		return s1;
	};

private:
	int a;
};

//左移运算符重载
ostream& operator<<(ostream& out, Student p1) {
	out << p1.a;
	return out;
}

int main()
{
	int a = 1;
	//Student s1;
	//cout << (s1++)++ << endl;
	//cout << s1 << endl;
	system("pause");
	return 1;
}

赋值运算符重载

#include<iostream>
#include<string>
using namespace std;

class Student {
public:
	int * age;
	Student(int a) {
		age = new int(a);
	};
	~Student() {
		if (age != NULL) {
			delete age;
			age = NULL;
		}
	};

	// 重构赋值运算符
	Student& operator=(Student& s1) {
		if (age != NULL) {
			delete age;
			age = NULL;
		}
		// *s1.age 解引用
		age = new int(*s1.age);
		return *this;
	}
};



int main()
{
	Student s1(10);
	Student s2(11);
	cout << "xx1: " << *s1.age << endl;
	cout << "xx1: " << *s2.age << endl;
	s1 = s2;
	cout << "xx2: " << *s1.age << endl;
	cout << "xx2: " << *s2.age << endl;
	system("pause");
	return 1;
}

等于和不等于运算符重载

#include<iostream>
#include<string>
using namespace std;

class Student {
public:
	string name;
	Student(string m_name) {
		name = m_name;
	};
	bool operator==(Student& s1) {
		if (s1.name == name) {
			return true;
		}
		else {
			return false;
		}
	};
};

int main()
{
	Student s1("Tom");
	Student s2("Tom2");
	if (s1 == s2) {
		cout << "s1 和 s2 是相等的" << endl;
	}
	else {
		cout << "s1 和 s2 是bu相等的" << endl;
	}
	system("pause");
	return 1;
}

仿函数重载

image-20201125095841307


#include<iostream>
#include<string>
using namespace std;

class Student {
public:
	int operator()(int a, int b) {
		return a + b;
	};
};



int main()
{
    // 匿名函数对象调用仿函数
	cout << Student()(10,11) << endl;
    //常规的仿函数调用
    //Student s1;
    //cout << s1(10,11) << endl;
	system("pause");
	return 1;
}

继承

继承中,先调用父类,再调用子类的构造函数, 析构函数则相反.

**语法: **

class 子类 : 继承方式 父类名 
 {
     ...
}

class Student: public People
{
    ...
}
#include "string"
#include <iostream>
#include "string"
using namespace std;

class A {
public:
	A() {
		cout << "aaa" << endl;
	};
};

class B: public A {
public:
	B() {
		cout << "bbbb" << endl;
	};
};

int main() {
	B b;
	return 1;
}

继承方式

  • 公共继承 public
  • 保护继承 protected
  • 私有继承 private

子类不管是哪几种继承方式都无法访问和继承父类的private(私有域).

公共继承:
子类通过public的方式继承父类,子类会同步父类中公共和保护域中的内容和域
保护继承:
通过protected的方式继承父类,子类会将父类中公共和保护域中的内容,赋值到自己的protected域中
私有继承:会将父类的protected和public域中的内容,赋值到自己的private域中

image-20201125105040614

#include<iostream>
#include<string>
using namespace std;


class People {
public:
	int age = 10;
	void func() {
		cout << "my wife:" << wife << endl;
	};
protected:
	string name = "Tom";
private:
	string wife = "玛丽";
};
class Student:public People {
};

int main()
{
	Student s1;
	s1.func();
	system("pause");
	return 1;
}

继承中的对象模型

子类继承父类,会将父类所有的内容拷贝一份,到自己的内存中, 包括私有,保护,公共的, 虽然私有的不能访问,但也会拷贝.

可以通过siziof(Son) 方法查看子类的大小, 来验证. 也可以通过以下工具

image-20201125160140399

继承同名成员处理方式

子类和父类中的成员同名, 且要访问父类的成员时, 加上作用域即可

语法: 子类实例化的变量名::父类变量名.某个方法或属性

#include "string"
#include <iostream>
using namespace std;

class Peo {
public:
	int age = 11;
	void func() {
		cout << "Peo age: " << age << endl;
	};
};

class Student: public Peo {
public:
	int age = 10;
	void func() {
		cout << "Student age: " << age << endl;
	};
};


int main() {
	Student s1;
	s1.Peo::func();

}

棱形继承

什么是棱形继承

棱形继承

#include "string"
#include <iostream>
#include "string"
using namespace std;

class A {
public:
	int age =10;
};

class B :virtual public A {
};

// 加上virtual后,就可以解决棱形继承问题
class C :virtual public A {
};

// 此时D存在属性重复的问题,即棱形继承
class D :public B, public C {
};

int main() {
	D s1;
	s1.B::age = 111;
	s1.C::age = 222;
	cout << "adsf1: " << s1.C::age << endl;
	cout << "adsf2: " << s1.B::age << endl;
    cout << "adsf3: " << s1.age << endl;
	return 1;
}