【高级程序设计语言C++】特殊类设计

  • 1. 请设计一个类,不能被拷贝
  • 2. 请设计一个类,只能在堆上创建对象
  • 3. 请设计一个类,只能在栈上创建对象
  • 4. 单例模式
    • 4.1. 饿汉模式
    • 4.2. 懒汉模式
    • 4.3. 懒汉模式和饿汉模式的区别

1. 请设计一个类,不能被拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

用C++11的话,可以使用特殊的语法来实现一个不能被拷贝的类。在C++11中,可以使用删除函数(deleted function)来禁用拷贝构造函数和拷贝赋值运算符。

下面是使用C++11实现一个不能被拷贝的类的示例代码:

class NonCopyable {
public:
    NonCopyable() {}
    ~NonCopyable() {}

    // 删除拷贝构造函数和拷贝赋值运算符
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

在这个示例中,通过在拷贝构造函数和拷贝赋值运算符的声明后面加上= delete来删除这些函数。这样一来,当其他地方尝试拷贝或赋值这个类的对象时,编译器会报错。

这种方法更加简洁,也更符合现代C++的风格。同时,使用删除函数可以提供更明确的错误信息,让开发者更容易理解问题所在。

2. 请设计一个类,只能在堆上创建对象

实现方式:

  1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

下面是一个示例代码,展示了如何设计一个只能在堆上创建对象的类:

class HeapOnly {
public:
    static HeapOnly* createInstance() {
        return new HeapOnly();
    }

    void destroyInstance() {
        delete this;
    }

    // 禁用拷贝构造函数和拷贝赋值运算符
    HeapOnly(const HeapOnly&) = delete;
    HeapOnly& operator=(const HeapOnly&) = delete;

private:
    HeapOnly() {}
    ~HeapOnly() {}
};

在这个示例中,HeapOnly类的构造函数和析构函数都是私有的,这样其他地方就无法直接创建或销毁HeapOnly类的对象。而通过静态成员函数createInstance(),可以在堆上创建一个HeapOnly对象,并返回指向该对象的指针。同时,destroyInstance()函数用于在堆上销毁HeapOnly对象。

此外,还禁用了拷贝构造函数和拷贝赋值运算符,以确保对象不能被拷贝。

使用这种设计,其他地方只能通过调用HeapOnly::createInstance()来创建对象,并且必须手动调用destroyInstance()来销毁对象。这样可以确保对象只能在堆上创建和销毁,而不能在栈上创建对象。

3. 请设计一个类,只能在栈上创建对象

这段代码定义了一个只能在栈上创建对象的类StackOnly。下面是对代码的解释:

class StackOnly {
public:
    static StackOnly CreateObj()
    {
        return StackOnly();
    }

    void Print() const
    {
        cout << "StackOnly::Print()" << endl;
    }

private:
    StackOnly()
    {}

    StackOnly(const StackOnly&) = delete;
};

StackOnly类具有以下特点:

  1. 构造函数StackOnly()是私有的,因此无法直接在外部创建对象。
  2. 静态成员函数CreateObj()被用于在内部调用私有的构造函数,并返回一个StackOnly对象。
  3. 公有成员函数Print()用于打印一条消息。

在main()函数中,我们可以看到如何使用这个类:

int main()
{
    StackOnly so1 = StackOnly::CreateObj();
    so1.Print();

    return 0;
}

在main()函数中,我们通过调用StackOnly::CreateObj()来创建一个StackOnly对象,并将其赋值给so1。然后,我们调用so1.Print()来打印一条消息。

请注意,由于构造函数是私有的,因此无法直接在栈上创建对象,如代码中注释所示。这是通过将构造函数声明为私有来实现的。

这样设计的目的是确保只能通过特定的方式创建对象,以强制对对象的创建进行控制。

4. 单例模式

4.1. 饿汉模式

在C++中,饿汉模式(Eager Singleton)是一种单例设计模式的实现方式。单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

饿汉模式的特点是在程序启动时就创建单例实例,并在整个程序的生命周期内保持不变。这意味着无论是否使用该实例,它都会被创建并占用内存。

class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

private:
	InfoSingleton()
	{}

	InfoSingleton(const InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;


	map<string, int> _info;
	// ...

private:
	static InfoSingleton _sins;
};

InfoSingleton InfoSingleton::_sins;

int main()
{
    InfoSingleton::GetInstance().Insert("张三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 15000);
	infosl.Insert("赵六", 12000);
	infosl.Insert("王五", 8000);
	infosl.Print();
	// ...

	return 0;
}

在这个例子中,InfoSingleton类是一个单例类。以下是饿汉模式的关键点:

  1. 类中的静态成员变量_sins是InfoSingleton类的唯一实例。
  2. **GetInstance()**是一个静态成员函数,用于获取单例实例。它返回一个引用,以便可以对单例实例进行操作。
  3. **构造函数****InfoSingleton()**是私有的,因此无法直接在外部创建对象。这样做是为了防止在程序中的其他地方创建多个实例。
  4. 复制构造函数和赋值运算符被删除,以防止通过复制或赋值操作创建多个实例。
  5. 在类定义之外,我们在全局范围内创建了一个InfoSingleton对象_sins,这样它就会在程序启动时被创建。

通过调用InfoSingleton::GetInstance(),我们可以获取对单例实例的引用,并对其进行操作。在您的代码中,我们使用InfoSingleton::GetInstance()来插入和打印员工信息。

饿汉模式的优点是实现简单,线程安全(因为实例在程序启动时就已经创建),并且可以保证全局唯一性。然而,它的缺点是可能会浪费内存,因为实例在整个程序的生命周期内都存在,即使不使用它。

4.2. 懒汉模式

在C++中,懒汉模式(Lazy Singleton)是一种单例设计模式的实现方式。与饿汉模式不同,懒汉模式在第一次使用时才创建单例实例。

class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		static InfoSingleton sinst;
		return sinst;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}

private:
	InfoSingleton()
	{
		cout << "InfoSingleton()" << endl;
	}

	InfoSingleton(const InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;


	map<string, int> _info;
	// ...
};

int main()
{
	infosingleton::getinstance().insert("张三", 10000);
	infosingleton& infosl = infosingleton::getinstance();
	infosl.insert("李四", 15000);
	infosl.insert("赵六", 12000);
	infosl.insert("王五", 8000);
	infosl.print();

	infosingleton::getinstance().insert("张三", 13000);
	infosingleton::getinstance().print();
	infosl.print();

	return 0;
}  

在这个例子中,InfoSingleton类是一个单例类。以下是懒汉模式的关键点:

  1. 类中的静态局部变量sinst是InfoSingleton类的唯一实例。它是在GetInstance()函数内部定义的,所以它的创建会被延迟到第一次调用GetInstance()时。
  2. GetInstance()是一个静态成员函数,用于获取单例实例。它返回一个引用,以便可以对单例实例进行操作。
  3. **构造函数****InfoSingleton()**是私有的,因此无法直接在外部创建对象。这样做是为了防止在程序中的其他地方创建多个实例。
  4. 复制构造函数和赋值运算符被删除,以防止通过复制或赋值操作创建多个实例。
  5. 在类定义之外,我们在GetInstance()函数内部创建了一个InfoSingleton对象sinst,这样它就会在第一次调用GetInstance()时被创建。

**懒汉模式的优点是实现简单,并且在第一次使用时才会创建实例,避免了可能的内存浪费。**然而,懒汉模式在多线程环境下可能存在线程安全问题。在C++11之前,静态局部变量的初始化是非线程安全的。但是在C++11之后,编译器保证了静态局部变量的线程安全性,因此可以在多线程环境下使用懒汉模式。

4.3. 懒汉模式和饿汉模式的区别

懒汉模式和饿汉模式在返回单例对象的函数上是相似的,都是返回一个静态的对象。但是它们的实现方式有所不同。

  1. **在懒汉模式中,单例对象在第一次使用时才被创建。**这意味着在调用获取单例对象的函数之前,单例对象并不存在。当第一次调用获取单例对象的函数时,会创建一个静态局部变量,该静态局部变量是单例对象,并且在整个程序运行期间只会创建一次。
  2. **而在饿汉模式中,单例对象在程序启动时就被创建。**这意味着单例对象在整个程序运行期间都是存在的,无论是否使用它。饿汉模式的实现方式是在类定义中直接创建一个静态成员变量,并在该变量的定义处初始化单例对象。

懒汉模式和饿汉模式是两种常见的单例模式实现方式,它们的区别主要体现在单例对象的创建时机和线程安全性上。

  1. 创建时机:
    • 懒汉模式:单例对象在第一次使用时才会被创建。在获取单例对象的函数中,会检查单例对象是否已经创建,如果没有则创建一个新的对象并返回。
    • 饿汉模式:单例对象在程序启动时就会被创建。在类定义中直接创建一个静态成员变量,并在该变量的定义处进行初始化。
  1. 线程安全性:
    • 懒汉模式:在多线程环境下,懒汉模式可能存在线程安全问题。如果多个线程同时调用获取单例对象的函数,并且单例对象尚未创建,那么可能会创建多个实例。为了解决这个问题,可以使用互斥锁等机制来保证线程安全。
    • 饿汉模式:饿汉模式在程序启动时就创建了单例对象,因此不存在线程安全问题。在多线程环境下,无论多少个线程同时调用获取单例对象的函数,都只会返回同一个已经创建好的对象。

选择懒汉模式还是饿汉模式取决于具体的需求和场景。懒汉模式的优点是实现简单,且只在需要时才会创建对象,节省了内存资源。但是需要注意处理多线程环境下的线程安全问题。饿汉模式的优点是线程安全,不需要额外的线程同步机制,但在程序启动时就会创建对象,可能会造成一定的资源浪费。

你可能感兴趣的:(c++)