Interview_C++_day25

如何控制一个类只能在堆或栈上创建对象

\(C\)++ 中创建对象的方法有两种,一种是静态建立,一个是动态建立。

  • 静态建立由编译器为对象分配内存,通过调用构造函数实现。这种方法创建的对象会在栈上。
  • 静态建立由用户为对象分配内存,通过 \(new\) 来实现,间接调用构造函数。这种方法创建的对象会在堆上。

只能从堆上分配对象:

当建立的对象在栈上时,由编译器分配内存,因此会涉及到构造函数和析构函数。那么如果无法调用析构函数呢?也就是说析构函数是 \(private\) 的,编译器会先检查析构函数的访问性,由于无法访问,也就防止了静态建立。

但这种方法存在一种缺点,就是把析构函数设成 \(private\) 后,如果这个类要作为基类的话,析构函数应该设成虚函数,而设成 \(private\) 后子类就无法重写析构函数,所以应该把析构函数设成 \(protected\)。然后额外设置一个接口来 \(delete\)

class Node {
public:
    Node(){};
    void Destroy() {
        delete this;
    }
protected:
    ~Node(){};
};

此时解决了静态建立的过程,但使用时,通过 \(new\) 创建对象,通过 \(Destroy\) 函数释放对象,为了统一,可以把构造函数和析构函数都设成 \(protected\),重写函数完成构造和析构过程。

class Node {
public:
    static Node* Create() {
        return new Node();
    }
    void Destroy() {
        delete this;
    }
protected:
    Node(){};
    ~Node(){};
};

只能从栈上分配对象:

同样的道理,只需要禁止通过动态建立对象就可以实现在栈上分配对象,所以可以重载 \(new\)\(delete\) 并设为 \(private\),使用户只能静态建立对象。

class Node {
public:
    Node(){};
    ~Node(){};
private:
    void* operator new(size_t t){}
    void operator delete(void* p){}
};

\(C\)++ 内存管理的内存池技术

每次 \(malloc\) 后,都会在分配空间的前后加上 \(4\) 个字节的额外 \(cookie\)。所以一次 \(malloc\) 就会浪费 \(8\) 字节内存。

通过内存池技术,重载 \(new\) 符号,使一开始使分配一大块数据空间,然后在将这部分空间一点一点的分给需要申请内存的对象,这样只会有一次大的 \(8\) 字节 \(cookie\),可以节省内存。


class Screen{
public:
    static void * operator new(size_t size) {
        Screen *p;
        if(!freeStore){ // 如果没有可以用的内存空间,则再分配一次
            size_t chunk = screenChunk * size;
            freeStore = p = reinterpret_cast<Screen*> (new char[chunk]);    // char 字节为 1
            for(; p != &freeStore[screenChunk - 1]; ++ p){
                p -> next = p + 1;
            }
            p -> next = NULL;
        }
        p = freeStore;
        freeStore = freeStore -> next;
        return p;
    }
    static void operator delete(void* p) {
        (static_cast<Screen*>(p))->next = freeStore;    // 把 delete 掉的内存空间直接插入到头部,加快插入时间
        freeStore = static_cast<Screen*>(p);
    }
private:
    Screen* next;
    static Screen* freeStore;   // 将可用内存用链表形式存储,存下链表头指针
    static const int screenChunk;   // 选择一次性分配的内存空间
private:
    int i;
};
Screen* Screen::freeStore = NULL;
const int Screen::screenChunk = 24;
int main() {
    // freopen("in", "r", stdin);
    Screen *p[100];
    for(int i=0; i<100; i++) {
        p[i] = new Screen;
    }
    cout << "size of Screen " << sizeof(Screen) << endl;
    for(int i=0; i<10; i++) {
        cout << "address of p[" << i << "] " << p[i] << endl;
    }
    return 0;
}
/*
重载 new :
size of Screen 8
address of p[0] 0x7caa18
address of p[1] 0x7caa20
address of p[2] 0x7caa28
address of p[3] 0x7caa30
address of p[4] 0x7caa38
address of p[5] 0x7caa40
address of p[6] 0x7caa48
address of p[7] 0x7caa50
address of p[8] 0x7caa58
address of p[9] 0x7caa60
*/

\(memcpy\) 的实现

\(memcpy\) 可以直接通过指针自增赋值,但如果被赋值对象指针地址在赋值对象地址内时,会因为地址的重合导致数据被覆盖,所以需要从末尾往前自减赋值。

为了加快速度还可以使用 \(4\) 字节赋值的方式

// 直接按字节进行 copy
void mymemcpy1(void* s, const void* t, size_t n) {
    char *ps = static_cast<char*>(s);
    const char *pt = static_cast<const char*>(t);
    if(ps<=pt && pt<=ps+n-1) {
        ps = ps+n-1;
        pt = pt+n-1;
        while(n--) {
            *ps-- = *pt--;
        }
    } else {
        while(n--) {
            *ps++ = *pt++;
        }
    }
}

// 加快速度,每次按 4 字节进行 copy
void mymemcpy2(void *s, const void *t, size_t n) {
    int *ts = static_cast<int*>(s);
    const int *tt = static_cast<const int*>(t);
    char *ps = static_cast<char*>(s);
    const char *pt = static_cast<const char*>(t);
    int x = n/4, y = n%4;
    if(ps<=pt && pt<=ps+n-1) {
        ps = ps+n-1;
        pt = pt+n-1;
        while(y--) {
            *ps-- = *pt--;
        }
        ps++, pt++;
        ts = reinterpret_cast<int*>(ps);
        tt = reinterpret_cast<const int*>(pt);
        ts--, tt--;
        while(x--) {
            *ts-- = *tt--;
        }
    } else {
        while(y--) {
            *ps++ = *pt++;
        }
        ts = reinterpret_cast<int*>(ps);
        tt = reinterpret_cast<const int*>(pt);
        while(x--) {
            *ts++ = *tt++;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/Jiaaaaaaaqi/p/12505730.html