cpp基础 2022-05-16
指针
声明一个指针
type *var-name;
eg.
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
注意,所有指针本身的实际数据类型都是一样的,任何指针都是一个代表内存地址的长的十六进制数
关于c++引用和指针
c++函数通过值传递的方式传参,函数的内部逻辑不会影响外部的变量,除非以引用或者指针的形式进行参数传递
注意:引用从一而终,而指针指向的对象可以改变。而且引用必须在声明同时初始化,而指针可以先声明,在任意需要的时间初始化。简单来说,引用就像专一的病毒一样,必须始终依赖同一宿主,而指针是独立的数据类型
new()
New会动态分配内存产生一个新对象,返回指向该对象首地址的指针
函数重载
C++允许写出若干个具有相同函数名的函数。一个或多个函数名相同,但是参数个数或参数类型不同的函数,叫做函数重载。例如,我们可以编写2个名为Max的函数,Max(int a, int b), Max(float a, float b),来求不同数据类型的最大值。
函数重载使得函数命名变得简单,在调用函数的时候,编译器会将调用函数时提供的实际参数和每个重载函数的参数对比,判断应该调用哪个函数
更加根本的原因是,C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中介的函数名称
类继承
继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员
虚基继承
class B: virtual public A
virtual public A 表示B类愿意共享它的基类A,A成为虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。虚基继承可以解决菱形继承问题
例如:
#include <iostream>
using namespace std;
//基类
class D
{
public:
D(){cout<<"D()"<<endl;}
~D(){cout<<"~D()"<<endl;}
protected:
int d;
};
class B:virtual public D
{
public:
B(){cout<<"B()"<<endl;}
~B(){cout<<"~B()"<<endl;}
protected:
int b;
};
class A: virtual public D
{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
protected:
int a;
};
class C:public B, public A
{
public:
C(){cout<<"C()"<<endl;}
~C(){cout<<"~C()"<<endl;}
protected:
int c;
};
int main()
{
cout << "Hello World!" << endl;
C c; //D, B, A ,C
cout<<sizeof(c)<<endl;
return 0;
}
Result is below:
Hello World!
D()
B()
A()
C()
40
~C()
~A()
~B()
~D()
普通继承和虚继承的内存模型
为什么基类指针可以指向派生类对象
派生类型之间的数据结构类似于这样:
BaseClass : [Base Data]
DerivedClass : [Base Data][Derived Data]
派生类型的数据附加在其父类之后,这意味着当使用一个父类型指针指向其派生类的时候,父类访问到的数据是派生类当中由父类继承下来的这部分数据,再往后的Derived Data是不可被基类指针访问的
基类指针指向派生类对象,永远指向的是基类部分数据的起始位置
虚继承的内存模型
对于普通继承,基类成员变量始终在派生类成员变量的前面
而对于虚继承,和普通继承相反,大部分编译器会把基类成员变量放在派生类成员变量的后面
BaseClass : [Base Data]
DerivedClass : [Derived Data][Base Data]
虚函数
虚函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基类定义的成员函数”这条规则反其道而行之的设计。对于某些函数,定义虚函数是为了允许用基类的指针来调用子类的这个函数。和虚函数关联的概念是虚函数表,关于虚函数表,可参考https://zhuanlan.zhihu.com/p/216258189?utm_source=wechat_timeline
虚函数声明如下:
virtual ReturnType FunctionName(Parameter){ do something; }
虚函数必须实现,不实现则编译报错
virtual修饰符强调父类的成员函数可以在子类中被重写。被virtual修饰的成员函数,不论他们是private、protect或是public的,都会被统一的放置到虚函数表中。对父类进行派生时,子类会继承到拥有相同偏移地址的虚函数表(相同偏移地址指,各虚函数相对于VPTR指针的偏移),则子类就会被允许对这些虚函数进行重载
虚函数和多态中的动态绑定在
c++中,我们在使用基类的指针调用虚函数时,就会发生动态绑定。所谓动态绑定,就是在运行时,虚函数会根据绑定对象的实际类型,选择调用函数的版本。通常的情况是,一个基类的指针指向一个派生类对象,使用这个基类指针调用虚函数就可以调用派生类对象中具体实现的虚函数
纯虚函数 - 类似golang interface
没有任何函数体,定义了纯虚函数的类是一个抽象类。定义纯虚函数是为了定义一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数
virtual void funtion()=0;
c++ 返回引用
A& func(int a, int b) { do... }
返回一个A类型的引用
使用extern公用全局变量
利用extern使c++多个源文件共用一个全局变量
例子:
头文件:state.h 源文件:state.cpp
其它源文件:t1.cpp t2.cpp t3.cpp, 这些源文件都包含头文件state.h
定义一个全局变量供这些源文件中使用,方法如下
- step1、在state.cpp中定义该全局变量:int a = 10;
- step2、在state.h声明全局变量: extern int a;
这样其它源文件就可以直接使用变量a,而无需再declare or define
https://stackoverflow.com/questions/10422034/when-to-use-extern-in-c