前几天晚上,给公司做机器人导航的舒少讲了几个常用的设计模式,主要是创建类型的工厂、建造者之类的。昨天晚上临下班他找我问了一个关于C++单例模式的一个问题。
问题是这同样的:
他看了一篇文章,里面实现了饿汉模式的单例,将构造函数私有化,提供公开静态的GetInstance()方法。这都毫无疑问,作者是这样写的代码。
#pragma once
class Singelton {
private:
static Singelton *single;
static Singelton single1;
Singelton(){
printf("Singelton create\n");
}
public:
static Singelton *GetSingelton();
static void print();
};
#include <iostream>
using namespace std;
Singelton *Singelton::single = new Singelton;
Singelton Singelton::single1 = Singelton();
void Singelton::print(){
cout<<"123"<<endl;
}
Singelton *Singelton::GetSingelton(){
return single;
}
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
Singelton * singelton = Singelton::GetSingelton();
singelton->print();
return 0;
}
舒少的问题在这一行:
Singelton *Singelton::single = new Singelton;
Singelton Singelton::single1 = Singelton();
明明构造函数已经私有化了,这里还明目张胆的调用~
折腾了一下:
问题答案是这样的,静态的公开变量,赋值的时候作用域是扩展到了后面的,整个赋值语句都在Singelton::
的上下文中。
static Singelton *single;
static Singelton single1;
所以可以调用构造函数,而且不论是new还是不用new.
问题就这样解决了吗?
不是的,伴随而来的又产生了新的疑问:
内存有堆栈之分,使用静态创建的对象存在于栈中,而使用动态创建的对象放入堆中,静态的变量存放在单独的静态区。
如果我不想让一个变量放在栈中,自己进行内存管理,可以使用私有化析构函数的方法,因为使用静态创建对象的时候,系统会判断该对象的析构函数是否能够调用,如果不能调用就会导致创建失败。
不过如果使用动态创建的方式,即:
new Singelton
那么这个对象的内存将创建在堆上,需要我们手动delete。
以上定义都没问题,反常识的是,当使用new进行创建对象的时候,系统会调用该对象的new操作,所以我们可以对这个操作进行重载:
像下面这个样子:
#pragma once
class Singelton {
private:
static Singelton *single;
static Singelton single1;
Singelton(){
printf("Singelton create\n");
}
public:
static Singelton *GetSingelton();
static void print();
void* operator new(size_t t){
printf("operate new\n");
}
void operator delete(void* ptr){
printf("operate delete\n");
}
};
一起看起来没问题,运行!
然后你发现,结果是这个样子的:
operate new
Singelton create
这意味着,是先调用new,后调用构造函数的,那问题就是没调用构造函数之前,为什么可以调用它的成员函数new?还是说new这玩意算不上成员函数?
问题的答案大概是这样子:
- 此new 非彼new,真正的new应该是一系列操作,包括分配空间,创建对象,返回等一系列骚操作
- 既然系统可以在没有产生对象调用构造函数,那系统一定可以在没有创建之前调用new的操作符重载内容
所以说,这只是一种规则而已,理解即可,不必纠结~
具体可以看一下new的源码,应该会比价通透一些。
受到了传统思维限制了,认为没有对象就不能调用其非静态成员函数~~
好好学习,天天向上~