cpp insight
2019-08-06 本文已影响0人
滩主
new 操作符
在堆上申请空间,返回首地址至%rax
cpp也容易被玩坏, 不要在c/c++中秀汇编,作死
使用%rbx保存类对象地址,若在构造函数里改变%rbx的值,程序crash
image.png
类函数调用
栈上的类对象,函数调用时传入栈地址
image.png
构造函数调用
给我们的编程抽象是类,但是实际都是函数调用、传参
image.png
子类赋值到父类
image.png虚函数表
带有虚函数的类实例化对象时,会添加一个指针指向该类的虚函数表
there are always 2 pointers leading the vtable: one for the multiple inheritance top pointer and another for RTTI. If your machine and compiler are 64 bits (you really should mention this; it's important), this coincides with 16 bytes of pointers
找到虚函数表位置
#include <iostream>
class Person
{
public:
virtual void foo()
{
std::cout << "foo" << std::endl;
}
virtual void bar()
{
std::cout << "bar" << std::endl;
}
};
typedef void (*FUNC)(void*);
int main()
{
Person p;
p.foo();
long* vptr = (long*)(*((long*)(&p)));
((FUNC)(vptr[0]))(&p);
((FUNC)(vptr[1]))(&p);
int i = sizeof(Person);
return 0;
}
类对象调用虚函数和类指针调用虚函数
image.pngclass Person
{
public:
virtual void foo() {}
virtual void bar() {}
};
class Student: public Person
{
public:
int col1;
virtual void foo() {}
virtual void bar() {}
};
int main()
{
Student p;
p.foo();
p.bar();
auto s = new Student();
s->foo();
s->bar();
(*s).foo();
(*s).bar();
Student &p_ptr = p;
p_ptr.foo();
p_ptr.bar();
}
编译器添加构造函数
image.png禁止对象生成在堆上
#include <stdlib.h>
#include <iostream>
class Resource
{
};
class NoHashObject
{
private:
void* operator new(size_t size)
{
return malloc(size) ;
}
void operator delete(void* pp)
{
free(pp) ;
}
public:
Resource* ptr ;
NoHashObject()
{
ptr = new Resource() ;
}
~NoHashObject()
{
delete ptr ;
}
};
int main()
{
// NoHashObject* o = new NoHashObject();
char* tmp = new char[sizeof(NoHashObject)];
NoHashObject* o = (NoHashObject*)tmp;
Resource** r = (Resource**)o;
*r = new Resource();
std::cout << *r << std::endl;
std::cout << o->ptr << std::endl;
delete *r;
delete tmp;
return 0;
}
构造函数和析构函数
当使用new操作符创建一个类类型对象时实际上发生了3个步骤:
调用函数operator new分配内存
调用构造函数进行构造对象
返回构造的对象的指针
当使用delete操作符释放一个类类型对象时实际上发生了2个步骤:
调用析构函数进行析构对象
调用函数operator delete释放内存
image.png
函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数
image.png
#include <iostream>
class Stu
{
public:
void foo(int)
{
std::cout << "Stu::foo(int)" << std::endl;
}
};
void foo(const Stu&,int a)
{
std::cout<< "foo(const stu&,int)" << std::endl;
}
void foo(Stu*,int a)
{
std::cout<< "foo(stu*,int)" << std::endl;
}
void foo(int a,const Stu&)
{
std::cout<< "foo(int,const stu&)" << std::endl;
}
void foo(const Stu&,double a)
{
std::cout<< "foo(const stu&,double)" << std::endl;
}
int main()
{
Stu s;
Stu& s1 = s;
foo(&s,100);
foo(s1,100);
s.foo(100);
s1.foo(100);
}
运算符重载
image.png继承的内存布局
image.pngvector内存布局
#include <iostream>
#include <vector>
int main()
{
std::vector<int> arr;
std::cout << sizeof(std::vector<int>) << std::endl;
for (int i=0;i<10;i++)
{
std::cout << arr.size() << " " << arr.capacity() << std::endl;
arr.push_back(i);
}
// &arr 栈上的地址
// &arr[0] 堆上的Base地址
// (void*)(*((int **)&arr)) 堆上的Base地址
std::cout << &arr << " "<< &arr[0] << " " << (void*)(*((int **)&arr)) << std::endl;
std::vector<int> arr1;
arr1 = arr;
std::cout << &arr1 << " "<< &arr1[0] << " " << (void*)(*((int **)&arr1)) << std::endl;
return 0;
}
都是函数调用
class Person {
public:
int age;
int weight;
char name[10];
Person()
{}
Person& operator=(Person& p)
{
return p;
}
Person(const Person& p)
{}
};
int main()
{
Person p;
Person p1 = p;
Person p2;
p2 = p;
return 0;
}
const 本意是只读,编译器会做相应优化
image.png内存布局
class Stu {
public:
// int score;
};
int main() {
Stu s = Stu();
Stu s1 = s;
Stu* s2 = new Stu();
Stu* s3 = &s;
Stu* s4 = &s1;
Stu s5[10];
s5[1] = Stu();
Stu* s6 = &s5[1];
return 0;
}
class Stu {
public:
class Person {
};
Person p;
int age;
// class Man {
// public:
// virtual void say() {
// }
// };
// Man m;
};
int main()
{
Stu s;
int a = sizeof(s);
s.age = 10;
int b = 20;
Stu s1;
s1.age = 30;
// s.p = Stu::Person();
// void* prt = &s.p;
return 0;
}
类的数组
编译器会帮助生成调用构造函数和析构函数
image.png
函数传参
image.png image.png使用栈传递参数
image.png
返回值
使用寄存器返回
image.png image.png image.png
栈对象复制
image.png异常的实现
决定类型
image.png
image.png
Advantages:
0-cost for entering try/catch block (as fast as if there was none)
0-cost for having a throw statement in a function (as long as it is not taken)
Disadvantage:
Slow in case of exception (10x slower than an if strategy) because the side tables are usually not in cache and then there are expensive computations to run to know which catch clause actually matches (based on RTTI)
It is a very popular strategy implement on both 32 bits and 64 bits platform for all major compilers... except MSVC 32 bits (if I remember correctly).
运算符呀运算符
image.pnglamda
匿名类的匿名对象,并重载函数调用运算符
https://hacpai.com/article/1545790136502