malloc 分配0字节
void* p = malloc(0);
分配0字节 所以不要搞多余内存的copy 比如:
char *q = (char *)p;
strcpy_s(q, 100 , "qweqweqweqw");
这就很危险 因为已经在操作不属于分配的内存
free(p);
这时候程序已经崩溃
所以malloc(0)返回的是一个有效的内存地址 你也不要去修改这个内存
所以就不要这么写 没意义
new类对象时加不加括号的差别:
class A{
public:
A(){};
int m;
}
int main(){
A *pa = new A();
A *pa = new A;
// 如果是个空类 ,那么两种写法没有区别
// 带括号的初始化会把一些和成员变量有关的内存清0
// 如果有构造函数 则这两个写法没有区别
}
-------------------------------------------------
int *p3 = new int; // 初值随机
int *p4 = new int(); // 初值0
int *p5 = new int(100); // 100;
-------------------------------------------------
new 干了什么?
new 是操作符
两件事:operator new() (调用malloc
一个是调用了类的构造函数 (有就调用 没有就不调用
delete:
调用析构函数
然后调用operator delete (调用free
new/delete 和malloc /free 有什么区别
new/delete 是操作符调用malloc/free malloc /free 是关键字
new/delete 会调用析构函数和构造函数
-------------------------------------------------
malloc 调用更底层和操作系统相关的函数
分配内存这个事 不是简单的分配四个字节 而是在四个字节周围 编译器做了很多处理 比如记录分配出去的字节数等等
造成了浪费。尤其时多次分配小的内存
内存池:
减少malloc次数,节约对内存的浪费
class A {
public:
static void* operator new(size_t);
static void operator delete(void* phead);
static int m_iCout; // 统计多少次new
static int m_iMallocCout; // 每malloc一次 ++;
private:
A* next; // 指向下一个对象
static A* m_FreePosi; // 总是指向一块可以分配出去的内存首地址
static int m_sTrunkCout; // 一次分配多少倍的该类内存
};
int A::m_iCout = 0;
int A::m_iMallocCout = 0;
A* A::m_FreePosi = nullptr;
int A::m_sTrunkCout = 5; // 一次分配五倍的该类内存作为内存池大小
void* A::operator new(size_t size) {
A* tmplink;
if (m_FreePosi == nullptr) {
// 为空 申请内存
size_t realsize = m_sTrunkCout * size;
m_FreePosi = reinterpret_cast<A*>(new char[realsize]);
tmplink = m_FreePosi;
for (; tmplink != &m_FreePosi[realsize - 1]; ++tmplink) {
// 链表 方便追踪
tmplink->next = tmplink + 1;
}
tmplink->next = nullptr;
// 将最后一个tmplink 指向空
++m_iMallocCout;
}
tmplink = m_FreePosi;
m_FreePosi = m_FreePosi->next; // 将m_FreePosi向下偏移 返回新的对象
// 当m_FreePosi为nullptr以后就重新申请内存
++m_iCout;
//
return m_FreePosi;
}
void A::operator delete(void * phead) {
(static_cast<A*>(phead))->next = m_FreePosi;
// 把当前对象的next指向最新的m_FreePosi
m_FreePosi = static_cast<A*>(phead);
// 这个对象重复使用
// 也就是说虽然说是delete但并没有调用释放内存
//new char[realsize] 并没有释放
// 析构函数倒是被调用
}
嵌入式指针:
一般应用在内存池相关的代码中: 成功使用其纳入是指针有个前提条件
类对象的sizeof不能小于4字节
借用对象原本的内存寻找下一个内存地址的手段
定位new;
在内存已经分配好的基础上 初始化已经新的对象 ( 调用构造函数
class A(){
public:
A(){}
A(int value):A(value){}
~A(){}
}
void *myPoint = (void *)new char[sizeof(A)];
A *myAobj = new (myPoint) A();
这里并不会分配额外的内存
// 带参数版本
void *myPoint = (void *)new char[sizeof(A)];
A *myAobj2 = new (myPoint) A(2);
//delete myAobj;
//delete myAobj2;
myAobj->~A();
myAobj2->~A();
delete[](void *)myAobj;
delete[](void *)myAobj2;
具体汇编代码
A *myAobj2 = new (myPoint) A(2);
operator new() // 这里并没有malloc
A::A()
重载定位new
void *operator new(size_t size, void *phead){
return phead
}
拷贝构造函数的临时性对象
class A{
public:
A(){cout << "拷贝构造函数被执行" <<endl;}
A(const &A tmpobj) {cout << "A::A()拷贝构造函数被执行" <<endl;}
~A() {cout << "析构函数被执行" <<endl;}
}
A operator +(const A& obj1, const A& obj2){
A tmpobj;
//...
return tmpobj;
}
int main(){
A myobj1;
A myobj2;
A result = myobj1 + myobj2;
}
//
拷贝构造函数被执行 //A myobj1;
拷贝构造函数被执行 //A myobj2;
拷贝构造函数被执行 //A tmpobj;
A::A()拷贝构造函数被执行 //return tmpobj; 产生一个临时对象,把tmpobj对象内容通过调用拷贝构造函数
把内容拷贝给临时对象 然后返回这个临时对象, 这个返回的临时对象直接构造到了result中
析构函数被执行 // A tmpobj;
析构函数被执行 // A myobj1;
析构函数被执行 // A myobj2;
析构函数被执行 // A result;
class A{
public:
A(){cout << "拷贝构造函数被执行" <<endl;}
A(const &A tmpobj) {cout << "A::A()拷贝构造函数被执行" <<endl;}
~A() {cout << "析构函数被执行" <<endl;}
A& operator=(const A& tmpobj) {
cout << "拷贝赋值运算符" <<endl;
return *this
}
}
A operator +(const A& obj1, const A& obj2){
A tmpobj;
//...
return tmpobj;
}
int main(){
A myobj1;
A myobj2;
A result;
result = myobj1 + myobj2;
}
拷贝构造函数被执行 //A myobj1;
拷贝构造函数被执行 //A myobj2;
拷贝构造函数被执行 //A result;
拷贝构造函数被执行 //A tmpobj;
A::A()拷贝构造函数被执行 //return tmpobj; 产生一个临时对象,把tmpobj对象内容通过调用拷贝构造函数
把内容拷贝给临时对象 然后返回这个临时对象
析构函数被执行 // A tmpobj;
拷贝赋值运算符 // 将临时对象拷贝赋值给 result;
析构函数被执行 // 释放这个临时对象
析构函数被执行
析构函数被执行
析构函数被执行
class A{
public:
A(){cout << "拷贝构造函数被执行" <<endl;}
A(const &A tmpobj) {cout << "A::A()拷贝构造函数被执行" <<endl;}
~A() {cout << "析构函数被执行" <<endl;}
A& operator=(const A& tmpobj) {
cout << "拷贝赋值运算符" <<endl;
return *this
}
}
A operator +(const A& obj1, const A& obj2){
A tmpobj;
//...
return tmpobj;
}
int main(){
A myobj1;
A myobj2;
myobj1 + myobj2;
}
构造函数
构造函数
构造函数
拷贝赋值运算符 // 临时对象
析构函数 // 析构 tmpobj
析构函数 // 析构临时对象
const char *p = (string("123") + string("456")).c_str();
// 右值产生的临时对象在本行结束后就会被销毁 所以这个代码有问题
string aaa = string("123") + string("123");
const char *q = aaa.c_str();
const string &aaa = string("123") + string("123");
----------------------------------------
模板及其实例化
模板在其调用的时候才会推断具体的类型 (vs2017
如果没有使用这个模板则不会推断
template<class T>
class ATL {
public:
enum ECURRSTATUS {
E_CS_Busy,
E_CS_Free
};
public:
T mi, mj;
ATL(T tmpi = 0,T tmpj = 0) {
mi = tmpi;
mj = tmpi;
}
};
ATL<int>::ECURRSTATUS n;
代码执行到这里依旧没有推断类型
n = ATL<int>::E_CS_Busy;
代码到这里也只是使用了ATL的int类型 也就是只使用了枚举类型
ATL<int>::ECURRSTATUS 编译器把这个只当作普通的枚举类型
template<class T>
class ATL {
public:
enum ECURRSTATUS {
E_CS_Busy,
E_CS_Free
};
static int m_sti;
public:
T mi, mj;
ATL(T tmpi = 0,T tmpj = 0) {
mi = tmpi;
mj = tmpi;
}
};
template <class T> int ATL<T>::m_sti = 12;
int main(){
cout <<ATL<int>::m_sti <<endl;
}
这里的类型也只是用到int并不涉及具体的类型推断
-------------------------------------------
template<class T>
class ATL {
public:
enum ECURRSTATUS {
E_CS_Busy,
E_CS_Free
};
static T m_sti;
public:
T mi, mj;
ATL(T tmpi = 0,T tmpj = 0) {
mi = tmpi;
mj = tmpi;
}
};
template <class T> T ATL<T>::m_sti = 12;
int main(){
cout <<ATL<int>::m_sti <<endl;
}
template <class T> T ATL<T>::m_sti = 12;这里的T只有在具体给定这个模板类型 并且在调用的时候
编译器才会确定这个语法是否合格
ATL<int> *pobj = NULL;
这种写法不会实例化具体的类
const ATL<int> &interATL = 0;
实例化了类模板
如果现在这个类中有函数 但是并没有具体调用则这个函数不会被实例化
如果这个类模板是在头文件中
那么在源文件中使用类模板就会初始化 不管到底调用不调用
在多个obj文件中可能产生多个重复的类模板对应的具体实例化类
但是在链接的时候直会保留一个ATL<int>类的实体,其余被忽略掉
虚函数也不会出现例外。即使没有被调用 也会被实例化,
因为有虚函数 编译器就会产生虚函数表 既然需要这个虚函数的地址 所以就必须实例化虚函数
显式实例化:
template class ATL<int>
显式实例化语法 会把类模板中 的所有内容都实例化出来,但是这种写法 铺展浪费
template void ATL<int>::func(); 具体实例化哪个函数
但是这并没有实例化ATL<int>本身 只是单独实例化具体的哪个函数
如何实现一个不能被继承的类
c++11 final
友元函数 + 虚继承 = 不能被继承的类
// 问题 :虚继承消耗大
class A{
private:
A(){};
int m;
}
class B:virtual public A{
}
为什么不能被继承?
虚继承的爷爷类最终是由孙子类调用
只有互为友元类才能调用private 所以孙子类无法实例化爷爷类 所以导致无法继承
类外调用私有虚成员函数
class A{
private:
virtual void virfunc(){
}
}
A obj;
(reinterpret_cast<void(*)())>(**int(**)(&obj)))();
就一个虚函数
类的地址跟虚函数指针的首地址相同
**int(**) 指针的指针 也就是虚函数表指针指向虚函数的地址
然后转换成void (*) () 函数指针
然后最外面() 调用