zoukankan      html  css  js  c++  java
  • 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){}
    };
    

    (memcpy)(memmove) 的实现

    (memcpy) 可以直接通过指针自增赋值,但要求源地址和目的地址无重合。

    void mymemmove1(void* s, const void* t, size_t n) {
    	char *ps = static_cast<char*>(s);
    	const char *pt = static_cast<const char*>(t);
    	while(n--) {
    		*ps++ = *pt++;
    	}
    }
    

    如果源地址和目的地址存在重合,会因为地址的重合导致数据被覆盖,所以要通过 (memmove) 来实现,需要从末尾往前自减赋值。

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

    // 直接按字节进行 copy
    void mymemmove1(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 mymemmove2(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++;
    		}
    	}
    }
    
  • 相关阅读:
    [PHP] yield沟通函数循环内外
    [Linux] scp本地服务器和远程服务器拷贝文件
    [Linux] 大数据库导出大文件统计并去重
    [Go] golang连接查询mysql
    [日常] 解决mysql不允许外部访问
    [Go] golang创建目录写文件判断文件
    [日常] imap协议读取邮件
    [Go] golang使用github里的imap类库
    [Go] golang无缓冲通道实现工作池控制并发
    [Go] golang的range循环遍历通道
  • 原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/12505730.html
Copyright © 2011-2022 走看看