所以,只要你的编译器兼容 C++11 标准,那么你应该使用 nullptr。
将父类的析构函数声明为虚函数,作用是用父类的指针删除一个派生类对象时,派生类对象的析构函数会被调用。
此时如果析构函数不加 virtual,那么 delete 父类指针的时候,子类的析构就不会被调用,某些情况下会导致内存泄漏。
我们之前讲述了什么是多态,还用了一个例子,将一个指针的类型做成强转,然后调用 func 函数,就会发现, func 函数会随着被强转的类型的变换而变换,这种函数的关联过程称为编联。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。
我们其实可以把一个员工强行转化成程序员,但是这就有可能出问题,就如同我们把 long long 转成 int 就有可能出现问题。
int main(int argc,char **argv)
{
Coder * coder = new Coder();
Staff * staff = coder; // 隐式转换就可以
Coder * coder = (Coder *)staff; // 必须显式转换
return 0;
}
我们之前已经讲过,可以为成员变量和成员函数设置权限修饰符来设置权限。但是这在继承的时候就会产生一个问题:子类中是否要保持对父类成员的权限,而 C++ 将这个选择权交给了程序员。
在 C++ 中,对父类成员的权限,子类中可以进行重新的定义,这个定义就是通过继承时候写的public 来实现的。
可以看到,我们在上述程序中使用了 public 修饰符,那么父类中的成员函数和成员变量将会保持父类的权限。这种是使用最广泛的继承方式,我们把它叫做公有继承。
private
用来指定私有成员。一个类的私有成员,不论是成员变量还是成员函数,都只能在该类的内部才能被访问
int main(int argc,char **argv)
{
A a;
a.a = 15; // 会报错,因为成员变量是 private 的
return 0;
}
public
用来指定公有成员。一个类的公有成员在任何地方都可以被访问。
int main(int argc,char **argv)
{
A a;
a.a = 15; // 不会报错,因为成员变量是 public 的
return 0;
}
protected
用来指定保护成员。一般是允许在子类中访问
要将对象分配到堆上,需要用到另外两个关键字,new 和 delete。new 用来分配对象,delete 用来删除对象。new 会返回一个指针,在使用完毕后,要通过 delete 把这个指针指向的地址释放掉。
#include "Staff.h"
int main(int argc,char **argv)
{
// 我们就这样实例化了三个员工
Staff * st1 = new Staff();
Staff * st2 = new Staff();
Staff * st3 = new Staff();
// 记得释放
delete st1;
delete st2;
delete st3;
return 0;
}
但是我们需要思考的是,什么情况下我们要返回一个指针,返回指针的时候需要我们注意些什么?
通常情况下,我们是希望为函数外提供一片内存,例如,我们可以给函数外面提供一个数组。
int * func()
{
int arr[] = {1, 2, 3, 4};
return arr;
}
但是这样写得话,程序会崩溃掉。原因是,arr 数组是一个局部变量,在 func 结束之后,其内存就被销毁掉了。此时在函数外面对其进行操作,自然会出问题。所以,要完成这类操作,我们需要把内存分配到堆内存上面。
int * func()
{
int * arr = (int *)malloc(4 * sizeof(int));
return arr;
}
这样就没有问题了,当然,既然是分配在了堆内存上,就要记得手动销毁。
int main(int argc,char **argv)
{
int * p = func();
free(p);
return 0;
}
字节序,就是 大于一个字节类型的数据在内存中的存放顺序。
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
我们现在有一个整数是258。用16进制表示是0x0102,然后我们把这个整数拆分成两个字节,第一个字节为 0000 0001,第二个字节为 0000 0010。
如果在一个使用大端字节序的电脑上,这个整数会被这样存放:
如果一个使用小端字节序的电脑上,这个整数的高字节就会存放在高地址上:
现在大部分的机器,都采用了小端字节序。但是在 IO 方面,则大部分使用大端字节序。例如,你要使用网络发送一个 int 类型的变量,要先把 int 转换成大端字节序,然后通过网络发送。
大端字节序又被称之为网络细节序。
<< 左移
各位全部左移若干位,高位丢弃,低位补 0 。
>> 右移
各二进位全部右移若干位,对无符号数,高位补 0 ,有符号数,各编译器处理方法不一样,有的补符号位,有的补 0 。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(int argc,char ** argv)
{
char source[20]={'a'};
char* originP= source;
*(originP+15)='c';
*(originP+16)='d';
*(originP+17)='e';
*(originP+18)='f';
*(originP+19)='g';
char target[5]={'b','b','b','b','b'};
char* targetP=target;
std::cout <<"origin value:"<< target[1] <<','<<target[1] <<std::endl;
memcpy(targetP,originP+15,5);
std::cout << "new value:"<<target[0] << ","<< target[1] <<std::endl;
return 0;
}
#include <iostream> #include <stdio.h> using namespace std; int main() { int a; cin>>a; cout<<a; return 0; }
将指针指向数组,不需要使用取址符&,并且表示的是指针执行数组第一个元素的地址:
int * p = arr 与 int * p = &arr[0]是相同的操作。
指针的加减法:
1. 想要让指针p指向第二个元素,只需要p+1就可以了。
注意:p+1的操作实际上是指针存放的地址+指针类型的大小。如果指针是一个int*的类型,则p+1就是向后移动4个字节的位置,如果是char*的类型,p+1则是向后移动2个字节的位置。
2. 指针支持p++、p--的操作
指针除了可以指向数组外,还可以指向一个结构体。
计算机中小数表示方法:
定点数容易造成空间浪费,
使用浮动的小数点:
浮点数按照 IEEE754标准存放的,符号位1位,指数8位,尾数23位:
浮点数存在精度问题,当场景中对精度要求较高,则不能使用浮点数存储。
求一个负数的二进制:
首先,找到其正数二进制表达形式;
然后,对最高位取1,获得原码;
其次,除最高位(符号位)不变外,其余位取反,获得反码;
最后,在反码的基础上加1,获得补码,也就是计算机对该负数的二进制表达。
计算机用补码表示负数,可以同正数一样对其进行加法操作。
RAII(resource acquisition is initialization)
class A
{
A();//创建资源
void fun();//操作资源
~A();//释放资源
};
浅拷贝
深拷贝
引用
与指针类似,相当于指针plus版
int &ra=a;
类中对象用指针需要用"->"来操作
A *pa=&a;
pa->data=20;
类中对象用引用时用"."来操作
A &ra=a;
ra.data=20;
二者其他区别:
引用必须有初始化值,指针可以赋值为nullptr
引用不可以二次赋值,指针则可以
A.B则A为对象或者结构体; 点号(.):左边必须为实体
A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针; 箭头(->):左边必须为指针;
编联
将一个指针的类型做成强转,然后调用 func 函数,func 函数会随着被强转的类型的变换而变换,这种函数的关联过程称为编联。
静态编联
在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定
动态编联
在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数
子类和父类相互转换
Staff * staff = coder; // 子类转父类隐式转换就可以
Coder * coder = (Coder *)staff; // 父类转子类必须显式转换
分部门的方式:多态公有继承
子类中使用 public 修饰符,那么父类中的成员函数和成员变量将会保持父类的权限。这种是使用最广泛的继承方式,我们把它叫做公有继承。
类的继承
类,就像是对某一类事物的抽象模版,而在某些场景下,我们希望对抽象的内容进行扩增,或者说更加具体化。为了完成这种关系,便是继承。
class Coder : public Staff
{
};
不可重载的运算符:
.
.* ->*
::
sizeof
?:
#
拷贝构造函数
Staff(const staff &staff);
memcpy(mem,staff.mem,20);
是C和C++使用的内存拷贝函数,函数原型为void *memcpy(void *destin, void *source, unsigned n);函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。
声明函数指针
int(*funcP)(int a,int b)
指针指向函数
int(*funcP)(int a,int b)=func1;
通过指针调用函数
(*funcP)(5);
int ret=(*funcP)(5);带返回值
函数中传入函数
int dowork(int a,int b,int (*callback)())
{
......
int ret=(*callback)();
}
回调函数/钩子函数 :应用到函数中插入代码/多线程中
指针声名
指针运算
地址 * > 内存
地址 &<内存
int*p=&a; //变量A的地址赋值给指针P,P指向变量A
指针和数组
int*p=arr;(数组变量名)
p+1(实际上是地址+类型大小)
p[1]=>*(p+1)指针偏移操作
指针和结构体
struct S *p=&s;
p->a=12;
p->b=22;
大端字节序(big endian):高位在前。在IO和网络传输方面采用方式,大端字节序又被称之为网络细节序
小端字节序(little endian):低位在前。大部分机器采用小端字节序
7
0111
1111原码
1000反码
1001补码