前几天晚上,给公司做机器人导航的舒少讲了几个常用的设计模式,主要是创建类型的工厂、建造者之类的。昨天晚上临下班他找我问了一个关于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 using namespace std; Singelton *Singelton::single = new Singelton; Singelton Singelton::single1 = Singelton(); void Singelton::print(){ cout<<"123"<<endl; } Singelton *Singelton::GetSingelton(){ return single; }
#include 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的源码,应该会比价通透一些。
受到了传统思维限制了,认为没有对象就不能调用其非静态成员函数~~
好好学习,天天向上~