C++中,类的对象建立分为两种,一种是静态建立,如A a; 另一种是动态建立,如 A *ptr = new A;
静态建立一个类对象,是由编译器为对象在栈空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。
动态建立一个类对象,是使用new运算符将对象建立在堆空间中。这个过程分为两步:第一步,执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步,调用构造函数构造对象,初始化这片内存空间;第三步,返回正确的指针。这种方法,间接调用类的构造函数。
一、只能建立在堆上
方法1:将类的析构函数设为private
方法2:将构造和析构函数设为protected,定义public static函数创建对象,定义public函数删除对象。[推荐使用]
类对象只能建立在堆上,就是不能静态建立类对象,即不能直接调用类的构造函数。容易想到将构造函数设为私有。在构造函数私有之后,无法在类外部调用构造函数来构造类对象(即无法静态建立),只能使用new运算符来建立对象。C++提供new运算符的重载,但new函数只能用于分配内存,无法提供构造功能,因此这种方法不可行。[ 因为静态建立/动态建立一个类对象时,会直接/间接调用类的构造函数,所以将构造函数设为私有的方法不可行。]
编译器管理了对象的整个生命周期,当对象建立在栈上时,由编译器分配内存空间,调用构造函数来构造栈对象。当对象使用完后,编译器调用析构函数来释放栈对象所占的空间。当编译器为类对象分配栈空间时,会先检查类的析构函数的访问性(不光是析构函数,只要是非静态的函数,编译器都会检查)。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。
class HeapOnly
{
public:
HeapOnly(){}
void destory(){delete this;}
private:
~HeapOnly(){}
};
int main()
{
//HeapOnly objectHeapOnly; // compile error
HeapOnly *ptrHO = new HeapOnly;
ptrHO->destory();
return 0;
}
使用HeapOnly objectHeapOnly; 创建类对象时,编译阶段会报错。
$ g++ classObject.cpp
classObject.cpp: In function ‘int main()’:
classObject.cpp:7: error: ‘HeapOnly::~HeapOnly()’ is private
classObject.cpp:13: error: within this context
上述代码代码有两个问题:
(1)无法解决继承问题。如果HeapOnly作为多态基类,则析构函数通常要设为virtual,然后在子类重写,以实现多态。因此析构函数不能设为private。
将析构函数设为protected可以有效解决这个问题,类外无法访问protected成员,子类可以访问。
(2)使用new创建一个类实例,却用destory函数释放对象,而不能使用delete删除对象。因为delete对象指针,会调用对象的析构函数,而析构函数在类外不可访问。
将构造函数设为protected,提供一个public的static函数来完成构造[静态成员函数可以通过类名直接访问](不使用new)。使用一个函数来构造,使用另一个函数来析构。
#include <iostream>
class HeapOnly
{
protected:
HeapOnly(){}
~HeapOnly(){}
public:
static HeapOnly* create()
{
return new HeapOnly();
}
void destory()
{
delete this;
}
void printLog()
{
std::cout<<"call printLog"<<std::endl;
}
};
int main()
{
HeapOnly *ptrHO = HeapOnly::create();
ptrHO->printLog();
ptrHO->destory();
return 0;
}
二、只能建立在栈上
方法:将operator new()函数设为private
只有使用new运算符,对象才能建立在堆上,因此禁用new运算符就可以实现类对象只建立在栈上。
class StackOnly
{
public:
StackOnly(){}
~StackOnly(){}
private:
void* operator new(size_t t){}
void operator delete(void* ptr){}
};
参考: