一.如何让类的对象只创建在栈上(或堆上)
这个是一个比较冷门的知识点,目前个人整理出了几种办法。
1.只分配在栈上:对new关键字重载并放在protected域
因为C++只有用new的关键字生成的对象才是放在堆上的,现在可以对new进行重载,将其放在private内,这样我们就调用不了new,这样也就可以限制程序将类对象分配到堆里头了。而由于new跟delete是配套出现的,重载new还需要重载delete。
参考链接:《More Effective C++》条款27:如何让类对象只在栈(堆)上分配空间?_hxz_qlh的博客-CSDN博客
class A
{
protected:
void* operator new(size_t t){} // 注意函数的第一个参数和返回值都是固定的
void operator delete(void* ptr){} // 重载了new就需要重载delete
public:
A(){}
~A(){}
};
2.只分配在堆上:将析构函数放在privete域
编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。因此,将析构函数设为私有,类对象就无法建立在栈上了。
class A
{
public:
A(){}
void destory(){delete this;}
private:
~A(){}
但是这个时候不能用delete,只能手动调用destory函数释放内存了。
3.借鉴单例模式思想:只用函数去生成对象或释放内存
将构造函数放置到private域当中,然后通过一个静态函数去控制分配在栈上还是堆上。
class Point1
{
protected:
Point1() {};//将构造函数私有化
public:
~Point1() {}
int k = 9;
static Point1 Produce1() {//控制分配在栈上
Point1 p;
return p;
}
static Point1* Produce2() {//控制分配在堆上
Point1 *p=new Point1;
return p;
}
};
4.使用placement new
参考自:C++中使用placement new_edenliuJ的博客-CSDN博客_c++ new placement
一般来说,使用new申请空间时,是从系统的“堆”(heap)中分配空间。申请所得的空间的位置是根据当时的内存的实际使用情况决定的。但是,在某些特殊情况下,可能需要在已分配的特定内存创建对象,这就是所谓的“定位放置new”(placement new)操作。
定位放置new操作的语法形式不同于普通的new操作。例如,一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr)A;申请空间,其中ptr就是程序员指定的内存首地址。考察如下程序。
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A's constructor" << endl;
}
~A()
{
cout << "A's destructor" << endl;
}
void show()
{
cout << "num:" << num << endl;
}
private:
int num;
};
int main()
{
char mem[100];
mem[0] = 'A';
mem[1] = '\0';
mem[2] = '\0';
mem[3] = '\0';
cout << (void*)mem << endl;
A* p = new (mem)A;
//或者
//A* p = new (mem)A();
//如果A有带参数的构造函数的话,也可以这样
//A* p = new (mem)A(value);
//STL的空间配置器将内存分配和构造调用分开来,需要用到placement new
cout << p << endl;
p->show();
p->~A();
getchar();
}
二、关于inline函数的优缺点
参考自:C++中的inline用法 - Boblim - 博客园
内联函数 —— C 中关键字 inline 用法解析_zqixiao_09的博客-CSDN博客_c语言 inline
1.inline函数的基本介绍
1)引入初衷:
- 在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。
- 定义在类中的成员函数缺省都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上inline,否则就认为不是内联的。
2)运行原理
- inline定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换(像宏一样直接将代码复制后展开),没有了调用的开销,效率也很高。
- 很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。
- inline可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。
2.相对宏定义的优点
即上面运行原理的点2和点3.
3.使用注意事项
尽管是否使用内联是编译器说的算(有的编译器会识别在内联函数超过一定长度后调用栈),但我们仍要小心使用,尤其别在里头放太多的代码或是调用复杂的迭代和递归,容易内存爆炸。
三、智能指针的循环引用问题
参考链接:C++ 智能指针(及循环引用问题)_Fly_bit的博客-CSDN博客
C++智能指针循环引用问题分析_swings_ss的博客-CSDN博客_智能指针循环引用
智能指针的循环引用问题,简单来说就是仅仅有shared_ptr会造成循环引用现象导致智能指针无法发挥其自动释放空间的功能。这时候需要使用weak_ptr来解决这种循环引用的现象。
除此之外补充一个:关于auto_ptr为什么要删除可参考: