数据结构
基础数据类型
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
修饰成员函数, 使参数在执行阶段确定地址
虚函数是动态多态的实现方式;多态即是,接口一样,参数一样,但是实现出来的效果不一样。
动态多态满足条件:
- 有继承关系
- 子类重写父类的虚函数
#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个函数
权限
- 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;
}
仿函数重载
#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域中
#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)
方法查看子类的大小, 来验证. 也可以通过以下工具
继承同名成员处理方式
子类和父类中的成员同名, 且要访问父类的成员时, 加上作用域即可
语法: 子类实例化的变量名::父类变量名.某个方法或属性
#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;
}