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

image.png

找到虚函数表位置

#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.png
class 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.png

vector内存布局

#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.png

lamda

匿名类的匿名对象,并重载函数调用运算符
https://hacpai.com/article/1545790136502

image.png image.png image.png image.png image.png

lamda

image.png
上一篇下一篇

猜你喜欢

热点阅读