【面试题002】java实现的单例模式,c++实现单例模式,实现禁止拷贝

时间:2021-08-20 01:30:34

【面试题002】java实现的单例模式,c++实现单例模式,实现禁止拷贝 

一 c++实现单例模式

保证一个类,在一个程序当中只有一个对象,只有一个实例,这个对象要禁止拷贝,注意这里要区别于java。否者的话一个程序当中就可能出现多个对象的拷贝。

我们要禁止拷贝,需要将拷贝构造函数以及等号运算符 声明为私有的,并且呢不提供他们的实现。这样子如果我们代码里面有拷贝构造的话,编译时候会出错。

仅仅这样子是不够的,我们必须将构造函数声明为私有的,这是为了防止外部呢,任意的构造对象。

既然我们将构造函数私有化了,外部就不能通过 Singleton s1; 来定义这样一个对象。那我们就需要提供一个接口让外部呢得到这样一个对象。

Singleton.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 
#include <iostream>
#include <memory>
using namespace std;

class Singleton
{
public:
    /*这个GetInstance是静态的由类直接调用的*/
    static Singleton *GetInstance()
    {

/*用这种方法会出现构造出来的这个对象什么时候释放的问题*/
        /*      if (instacne_ == NULL)
                {
                    instacne_ = new Singleton;
                }
                return instacne_;*/

/* 把裸指针用智能指针来管理
         * 智能指针是重载了点号运算符的,我们访问类本身的get()方法,获得裸指针
         */
        if (!instacne_.get())
        {
            instacne_ = auto_ptr<Singleton>(new Singleton);
        }

return instacne_.get();
    }

~Singleton()
    {
        cout << "~Singleton ..." << endl;
    }
private:
    // 禁止拷贝--构造函数和等号运算符声明为私有的,并且不提供实现。
    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);

// 将构造函数说明为私有的
    Singleton()
    {
        cout << "Singleton ..." << endl;
    }
    /*这实际上是一个静态的类对象,这里仅仅是引用性说明,她的定义应该在类的外边*/
    static auto_ptr<Singleton> instacne_;
};

/*定义性说明*/
auto_ptr<Singleton> Singleton::instacne_;

1
2
3
4
5
6
7
8
9
10
11
12
 
int main(void)
{
    //Singleton s1;
    //Singleton s2;

Singleton *s1 = Singleton::GetInstance();
    Singleton *s2 = Singleton::GetInstance();

//Singleton s3(*s1);        // 调用拷贝构造函数

return 0;
}

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
 
.PHONY:clean  
CPP=g++  
CFLAGS=-Wall -g  
BIN=test  
OBJS=Singleton.o  
LIBS=  
$(BIN):$(OBJS)  
    $(CPP) $(CFLAGS) $^ -o $@ $(LIBS)  
%.o:%.cpp  
    $(CPP) $(CFLAGS) -c $< -o $@  
clean:  
    rm -f *.o $(BIN)  

运行结果

Singleton ...
~Singleton ...

裸指针呢,用智能指针来管理静态的一个类对象,当整个程序结束的时候,静态对象也就被销毁了,

那么静态对象的销毁就会导致这个类对象的析构函数被调用,

instance_.get()
这个get()方法是 智能指针提供的是吧
auto_ptr中有个get方法
还有你给的析构函数  里面并没有释放 那个指针所指的资源 
是程序结束的时候,由智能指针来释放的
智能指针变量 本身不是堆区内存,
智能指针对象销毁的时候,智能指针对象的析构函数会去销毁它所包裹的堆对象

只不过说 智能指针释放了,静态的变量的时候会调用这个变量的析构函数

析构函数是,delete的时候调用的,这个delete操作是在智能指针对象的析构函数中调用的,从而引发了堆对象的析构
如果正常调用析构函数的情况,析构函数你是需要自己是释放对象的资源的,(因为是堆对象,堆上的东西自己释放)
  1. //Singleton s1;
  2. //Singleton s2;
  3. //Singleton s3(*s1);        // 调用拷贝构造函数

这些情况下都是错误的,

运行结果:

g++ -Wall -g -c Singleton.cpp -o Singleton.o
Singleton.cpp: 在函数‘int main()’中:
Singleton.cpp:36:2: 错误: ‘Singleton::Singleton(const Singleton&)’是私有的
Singleton.cpp:59:18: 错误: 在此上下文中
Singleton.cpp:57:13: 警告: 未使用的变量‘s2’ [-Wunused-variable]
make: *** [Singleton.o] 错误 1

二,实现禁止拷贝

Nocopyable这个类如何保证禁止拷贝,她的实现和Singleton类的实现差不多。

Noncopyable.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 
#include <iostream>
#include <memory>
using namespace std;

class Noncopyable
{
protected:
    Noncopyable() {}
    ~Noncopyable() {}
private:
    Noncopyable(const Noncopyable &);
    const Noncopyable &operator=(const Noncopyable &);
};

class Parent : private Noncopyable
{
public:
    Parent()
    {

}
    Parent(const Parent &other) : Noncopyable(other)
    {

}
};

class Child : public Parent
{
public:
    //Child(const Child& other)
    //{

//}
};

int main()
{
    /*这两种情况都是失败的*/
    //Parent p1;
    //Parent p2(p1);
    // 要调用Parent拷贝构造函数,
    //Parent构造函数又调用Noncopyable的拷贝构造函数

Child c1;
    Child c2(c1);
    return 0;
}

注意:

private 是实现继承,并不是为了继承她的接口

public 是接口继承

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
 
.PHONY:clean  
CPP=g++  
CFLAGS=-Wall -g  
BIN=test  
OBJS=Noncopyable.o  
LIBS=  
$(BIN):$(OBJS)  
    $(CPP) $(CFLAGS) $^ -o $@ $(LIBS)  
%.o:%.cpp  
    $(CPP) $(CFLAGS) -c $< -o $@  
clean:  
    rm -f *.o $(BIN)  
运行结果:
1
2
3
4
5
 
g++ -Wall -g -c Noncopyable.cpp -o Noncopyable.o
Noncopyable.cpp: 在复制构造函数‘Parent::Parent(const Parent&)’:
Noncopyable.cpp:11:2: 错误: ‘Noncopyable::Noncopyable(const Noncopyable&)’是私有的
Noncopyable.cpp:23:49: 错误: 在此上下文中
make: *** [Noncopyable.o] 错误 1

这里我们需要注意的地方是,

对于构造函数来说,如果基类有默认构造函数,即使我们没有写 :Noncopyable()这句话,他也是会自动调用基类的默认构造函数的,

但是拷贝构造函数就不一样啦,如果我们没有写 :Noncopyable(other)这句话,是不会调用基类的拷贝构造函数的,

当然如果基类没有默认的构造函数,那么这个时候呢,一定要在成员列表中给出对基类构造函数的调用。

JAVA实现的单例模式:

读取配置文件,并且实例化了一个对象,这个对象保证只有一个。

 Java Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 
package com.ebupt.ebms.conf;

/**
 * @author zling Create on 2011-3-7
 * @version 1.0
 */
public class MainConfig
{
    private String logPath = ""; // 上传的各种日志的本地路径

private static MainConfig instance = new MainConfig();//懒人模式

public static MainConfig getInstance()
    {
        if (instance == null)
            instance = new MainConfig();
        return instance;
    }

public String string()
    {

StringBuffer sb = new StringBuffer();
        sb.append("logPath : ").append(logPath).append("\n");
        return sb.toString();
    }

public String getLogPath()
    {
        return logPath;
    }

public void setLogPath(String logPath)
    {
        this.logPath = logPath;
    }

}