c++基础知识

面向对象语言三大特征:

封装,多态,继承

封装:

1、将函数定义到结构体内部,就是封装.

2、编译器会自动传递结构体的指针给函数.

类:

带有函数的结构体,称为类.

成员函数:

结构体里面的函数,称为成员函数.

 

this指针:

c++中默认传递一个对象首地址,这个地址就是this指针

thie指针特点

1、你用或者不用,它就在那里

2、参数个数确定的时候,用ecx来传递

3、参数个数不确定的时候,最后一个传递(参见不定长参数)

4、this指针不能做++ -- 等运算,不能重新被赋值.

5、this指针不占用结构体的宽度.

 

构造函数的特点:

1、与类同名

2、没有返回值(指的是前面连void都不能有)

3、创建对象的时候执行

4、主要用于初始化

5、可以有多个(最好有一个无参的),称为重载 其他函数也可以重载

6、编译器不要求必须提供

 

 

析构函数的特点:

1、只能有一个析构函数,不能重载

2、不能带任何参数

3、不能带返回值

4、主要用于清理工作

5、编译器不要求必须提供

6.  不需要自己调用该函数,编译器自己帮我们最后执行

在函数名前面加一个~,表示是析构函数

 

 继承

1、什么是继承?

继承就是数据的复制

2、为什么要用继承?

减少重复代码的编写

如果是Teacher、Student继承了Person

3、Person 称为父类或者基类

4、Teacher、Student称为子类或者派生类

Student s   Person t

5、t和s可以称为对象或者实例.

6、可以用父类指针指向子类的对象.(绝对安全)

 

多层继承:允许多重继承,即子—>父—->爷(s->t->v)

如果子和父中有相同变量(a) 使用 s::a  t::a 来告诉编译器这个a到底是谁的参数

 

多重继承:(微软不推荐,给编译器工作增大)

即有多个父类

struct Z:X,Y

 

权限控制

将定义与实现分开:

将定义与实现分离,代码会有更好的可读性

可以在头文件中 写上类中变量,函数申明

在.cpp文件中写上函数具体实现  xx::FunctionName   表示这个函数名是xx类里面的

1、xxx.h 只是一个文件,可以是任何的后缀名,如果你愿意,

2、#include 的作用只是把里面的内容复制过来 仅此而已.

如:#include "abc.exe"

3、xxx.h 与 xxx.cpp并不要求一定同名

 

 

 

public private的使用

public的意思是,这个成员哪里都可以用,不用担心被修改,所以,一旦发布成public的成员,是不能(不应该)够改名字的.

private的意思是,这个成员只用于(类的)内部使用,不要在其他的地方使用.

 

1、对外提供的函数或者变量,发布成public的 但不能随意改动.

2、可能会变动的函数或者变量,定义成private的 这样编译器会在使用的时候做检测.

3、只有结构体内部的函数才可以访问private的成员.

4、public/private可以修饰函数也可以修饰变量.

 

 

 

private真的不能访问吗?

使用private只是在编译器层面上限制访问  在底层 汇编角度来说 有没有private反汇编代码都是一样的

所以我们使用指针同样可以访问

 

private修饰的成员与普通的成员没有区别 只是编译器会检测.

private修饰的成员只要自己的其他成员才能访问

 

class与struct的区别

成员区别

编译器默认class中的成员为private 而struct中的成员为public

继承区别

父类中的程序继承后默认变成private属性

如果不希望改变成员的属性:

class Sub:public Base

填上public,否则默认private

继承的时候会先执行父辈的构造函数

 

private是否被继承

1、父类中的私有成员是会被继承的

2、只是编译器不允许直接进行访问

用指针访问

 

 

 

虚函数表

1、通过对象调用时,virtual函数与普通函数都是E8 Call

对象调用:

Student p;
p.study();

 

 这就没体现多态嘛,自然就算不上是动态绑定

2、通过指针调用时,virtual函数是FF Call,也就是间接Call

指针调用:

 Person* s = new Student();
 s->study();

 

 

 

 多态,所以动态绑定,运行时才知道要调的是那个函数,所以FFcall,先存个地址

1、当类中有虚函数时,会多一个属性,4个字节

2、多出的属性是一个地址,指向一张表,里面存储了所有虚函数的地址

 

绑定

绑定就是将函数调用与地址关联起来.

前期绑定/静态绑定:编译完时就已经绑定

后期绑定/动态绑定:在调用时才绑定,有虚表,virtual虚函数

 

1、只有virtual的函数是动态绑定.

2、动态绑定还有一个名字:多态

 

重载

一个类中:

两个重载函数必须在下列一个或两个方面有所区别:

1、函数的参数个数不同。

2、函数的参数类型不同或者参数类型顺序不同

举例:

void Breath()
    {
        printf("学生应该呼吸新鲜的空气\n");
    }
    void Breath(string whe)
    {
        printf("学生应该呼吸whe新鲜的空气\n",whe);
    }

 析构函数重载:

Student()
    {
        printf("Student已经初始化\n");
    }
Student(int grade, int id)
  {
      this->grade = grade;
      this->id = id;
  }

重写(覆盖)

重写(覆盖):是指派生类(子类)中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类(父类)中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用基类函数。重写的基类中被重写的函数必须有virtual修饰:

 1 class Person
 2 {
 3 private:
 4     int age;
 5     string name;
 6     static int right;
 7 public:
 8     Person()
 9     {
10         printf("person已经初始化\n");
11     }
12     int GetAge()
13     {
14         return this->age;
15     }
16     void SetAge(int age)
17     {
18         this->age = age;
19     }
20     string GetName()
21     {
22         return this->name;
23     }
24     void SetName(string name)
25     {
26         this->name = name;
27     }
28     void study()
29     {
30         printf("人可以学习!\n");
31     }
32     void eat()
33     {
34         printf("人可以吃饭\n");
35     }
36 
37 };
38 
39 class Student:public Person
40 {
41 private:
42     int grade;
43     int id;
44 public:
45     Student()
46     {
47         printf("Student已经初始化\n");
48     }
49     Student(int grade, int id)
50     {
51         this->grade = grade;
52         this->id = id;
53     }
54     int GetGrade()
55     {
56         return this->grade;
57     }
58     int SetGrade(int grade)
59     {
60         this->grade = grade;
61     }
62     int GetId()
63     {
64         return this->id;
65     }
66     void SetId(int id)
67     {
68         this->id = id;
69     }
70     void Breath()
71     {
72         printf("学生应该呼吸新鲜的空气\n");
73     }
74     void study()
75     {
76         printf("学生是学习的大好时刻\n");
77     }
78 };
79 int main()
80 {
81     Person* s = new Student();
82     s->study();
83     
84 }

这里先没用virtual修饰:

 

 

 多态就没有得到体现,要重写就必须要加上virtual

virtual void study()
    {
        printf("人可以学习!\n");
    }

纯虚函数

有virtual修饰的就是虚函数,虚函数意义在于可能要重写,但这是可能。假设我们希望描述大雁和老虎的行为,定义一个父类"animal",但我们无法直接描述,比如大雁是"飞翔",老虎是”奔跑“,那么我们如果定义父类时就没法明确的写出他们的行为到底是什么,因此有了纯虚函数:"我知道子类要继承使用这个方法,但我没法具体给出",纯虚函数是强制重写,不给出方法体,子类自己去实现。

实现方式:

virtual void study() = 0;

多态:

1.动态绑定

2.体现出了不同的行为

模板:

使用template<class T>来声明一个模板

T是未确定的类型,让我们可以使用不同的类型

 

引用类型

我们日常使用指针时,有可能一些意外的操作,将本不该指向的地址作为了指针的值,这样有不安全的风险,使用"引用"  int& x;  就是讲指针中的* 改为&  ,这样设计的好处在于:编译器不让你改这个指针的地址,增强了安全性

但从底层来说,汇编代码没有区别,实际上就是编译器限制你修改

使用x = 10;  来直接给x这个地址中的值赋值  ,而不是修改地址

 

1、引用类型是C++里面的类型

2、引用类型只能赋值一次,不能重新赋值

3、引用只是变量的一个别名,使用的时候按照变量去使用

4、引用可以理解成是编译器维护的一个指针,但并不占用空间

5、使用引用可以像指针那样去访问、修改对象的内容,但更加安全.

 

友元函数

friend void Print(const  Person& refPer);

在类中函数前添加一个friend,表示该函数为该类的朋友

这样这个函数也可以访问类的私有成员

(也可以定义一个友元类)

 

什么情况下需要友元函数:

(1) 运算符重载的某些场合需要使用友元.

(2) 两个类要共享数据的时候.

友元函数和类的成员函数的区别:

(1) 成员函数有this指针,而友元函数没有this指针

(2) 友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友

 

运算符重载:

有时需要一些针对特定类型进行操作的运算符

Number operator+(const Number& p);

 

1、运算符重载就是函数替换

2、. :: ?: sizeof # 不能重载

 

new delete

是c++中的在堆中分配空间,释放空间的关键词

在底层,与malloc free没有区别,都是调用的系统API kenel32这个dll中的 heapalloc 和heapfree

在使用时,new int[10]   对应的是delete[]   方括号里不需要写值

否则只会释放数组中的一个单元,本质delete是用一个循环free

 

 

Vector(动态数组)

1、本质就是一个数组

2、可以动态扩充容量

3、支持下标方法,查询性能好

4、新增数据和删除数据较差

 

 

 

链表:

特点:

1、数据分散存储

2、查询性能没有Vector好

3、新增与删除的性能好于Vector

 

有单向链表,循环链表,双向链表

命名空间

主要是为了彻底解决相同名字冲突的问题主要是,class重名

创建一个命名空间

namespace x

{

  int y = 0;

  void func();

  class Test

  {};

}

里面可以有类,函数,全局变量,当已经定义在命名空间后,使用必须加上命名空间名称,如x::func()

或者使用using namespace x;

static关键字

在面向过程中(c)

如果声明一个static的局部变量,那么这个局部变量变成全局变量,并且只能该函数访问,为私有的全局变量

在函数或变量前声明static

static void add(int x,int y);

static int x;

那么别的文件就无法访问这个函数或者这个变量

在面向对象中(c++)

用static声明一个变量就决定了这个变量是一个全局变量

并且只能由当前类中的函数使用,但这个变量已经不在类中了

同全局变量相比,使用静态数据成员有两个优势:

1.避免命名冲突;

2.可以实现信息隐藏;

静态成员函数

仅仅是为了方便

可以定义一个静态函数在一个类中,当我们想访问的时候就可以直接通过:类名::函数名() 调用,无需创建对象来调用

static关键词的经典应用:单子模式

有些时候我们希望定义的某些类只能有一个对象存在(因为一个对象就已经足够了),该如何进行限制呢?

实现思路:

1、禁止对象随便被创建

2、保证对象只有一份存在

 

posted @ 2021-03-29 20:56  Punished  阅读(194)  评论(0编辑  收藏  举报