电子商务网站建设需要注意什么,企业网页设计,国内最好的软件网站建设,如何进入网站管理页面1.请设计一个类#xff0c;不能被拷贝
设计思路
拷贝只会使用在两个场景中#xff1a;拷贝构造函数以及赋值运算符重载#xff0c;因此想要让一个类禁止拷贝#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C98 的做法
将拷贝构造函数与赋值运算符… 1.请设计一个类不能被拷贝
设计思路
拷贝只会使用在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C98 的做法
将拷贝构造函数与赋值运算符重载只声明不定义并且将其访问权限设置为私有即可。不定义则拷贝操作无法实际的实现设置成私有避免公有被调用出来实现
class A
{
private:A(const A);A operator(const A);
};原因
设置成私有如果只声明没有设置成private用户自己如果在类外定义了就可以不 能禁止拷贝了只声明不定义不定义是因为该函数根本不会调用定义了其实也没有什么意义不写反而还简单而且如果定义了就不会防止成员函数内部拷贝了。 C11 的做法
C11 扩展 delete 的用法delete 除了释放 new 申请的资源外如果在默认成员函数后加上 delete表示让编译器删除掉该默认成员函数。
class A
{A(const A) delete;A operator(const A) delete;
}; 2.请设计一个类只能在堆上创建对象 方法一构造函数私有化
构造、拷贝和赋值私有化将类的构造函数私有拷贝构造和赋值声明成私有。防止别人调用拷贝在栈上生成对象。或者将构造私有化将拷贝和赋值 delete 禁用同一提供对外接口提供一个静态的成员函数在该静态成员函数中完成堆对象的创建 这个有点封装的味道了将一些功能封装起来自己提供对外接口就可以控制外界可以使用的功能控制权限 为什么要设置成静态函数
关于为什么对外功能接口要设置成静态函数
思路
1、首先我们将构造函数私有化拷贝构造和赋值 delete 禁用掉外界就不可以调用这几个函数在栈上构造一个对象 2、其次该对外功能接口函数 也是成员函数调用一个成员函数需要一个对象来调用但是我们这里都没有创建对象正等着该功能函数来创建对象呢何来一个对象 3、这里就产生先有鸡还是先有蛋的问题 4、因此就需要避免使用通过对象调用的方式 可以设置成 静态成员在外部通过类域指定调用这时静态成员的特性就无需通过对象调用
class HeapOnly
{
public:// 设置成静态函数static HeapOnly* CreateObj() {return new HeapOnly;}// 将拷贝与赋值重载都使用 delete 禁用掉HeapOnly(const HeapOnly) delete;HeapOnly operator(const HeapOnly) delete;
private:// 将构造函数设置成私有避免外部调用在栈上构造对象HeapOnly() {};
};int main() {HeapOnly* p HeapOnly::CreateObj(); // 通过类域调用类的静态成员return 0;
}方法二析构函数私有化
注释都解释清楚了
// 析构函数私有化
class HeapOnly
{
public:void Destroy() {delete this;}
private:// 将析构函数设置成私有~HeapOnly() {};
};int main() {HeapOnly obj; // 报错创建一个类对象销毁时会自动调用析构但是这里调用不了因为析构函数被禁用了因此也不允许这个对象被创建出来HeapOnly* p new HeapOnly(); // 不报错new 出来的对象不会自动调用析构需要手动 deletedelete p; //报错调用不了析构 p-Destroy(); // 通过类中的功能接口销毁对象return 0;
}3.请设计一个类只能在栈上创建对象
这个比限制只能在堆上还要麻烦一些些
实现第一步构造函数私有化
class StackOnly
{
public:// 对外功能接口该函数可以调用私有成员构造函数创建对象返回匿名对象static StackOnly CreateObj() {return StackOnly();}private:// 将构造函数设置成私有避免外部通过 new在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 StackOnly::CreateObj();StackOnly* p_obj2 new StackOnly(obj1); return 0;
}
禁掉了 构造函数还可以走拷贝构造的路通过CreateObj() 函数先创建一个对象出来再 拷贝new 生成一个新对象 因此还要完善
实现第二步拷贝与赋值用 delete 禁用掉
class StackOnly
{
public:// 对外功能接口该函数可以调用私有成员构造函数创建对象返回匿名对象static StackOnly CreateObj() {return StackOnly();}// 将拷贝与赋值重载都使用 delete 禁用掉StackOnly(const StackOnly) delete;
private:// 将构造函数设置成私有避免外部通过 new在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 StackOnly::CreateObj();//StackOnly* p_obj2 new StackOnly(obj1); return 0;
}但是
将 拷贝构造禁用掉会导致 CreateObj 函数失效因为该成员函数返回的是局部对象需要拷贝生成临时对象而拷贝构造失效导致生成失败
因此还要完善
实现第三步operator new 用 delete 禁用掉
可以尝试从 new 的本质入手new 全局函数 operator newmalloc抛异常 构造
我们若自己显式实现 operator new 则 new 优先使用我们自己的
因此可以在这里将 new 的 operator new 禁掉使得 new 无法调用 // 只能在栈上创建对象
class StackOnly
{
public:// 该函数可以调用私有成员构造函数创建对象返回匿名对象static StackOnly CreateObj() {return StackOnly();}// 可以使用/*void* operator new(size_t size) {return malloc(size*sizeof(StackOnly));}*/void* operator new(size_t size) delete;void operator delete(void* p) delete;
private:// 将构造函数设置成私有避免外部通过 new在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 StackOnly::CreateObj();StackOnly* p_obj2 new StackOnly(obj1); // 报错return 0;
} 但是问题又回来了
只要没有将 拷贝构造禁用掉还是可以通过拷贝构造创建一个在静态区的对象
StackOnly obj1 StackOnly::CreateObj();static StackOnly obj3(obj1); // 不报错同时还可以通过 移动构造 创建静态区的对象
StackOnly obj1 StackOnly::CreateObj();static StackOnly obj4(move(obj1));这里为什么创建静态区的对象 仅仅是将栈区对象区别开
实现最终大法直接使用返回对象进行操作
既然我们的目的是设计一个只能在栈上对象的类 我们直接从这里思考我们先将拷贝、赋值、移动构造私有化 或 delete 禁用。 既然直接将 CreateObj() 函数返回的匿名对象拷贝给新对象会触发拷贝或移动构造
StackOnly obj1 StackOnly::CreateObj(); // 这里会触发拷贝或移动构造干脆别拷贝给新对象而是直接使用这个 匿名对象进行操作 其实这个方法有点取巧但是不也是达到了题目要求吗
int main() {StackOnly::CreateObj().Print(); // 直接使用该返回对象进行操作//StackOnly obj1 StackOnly::CreateObj(); // 会触发拷贝或移动构造//StackOnly* p_obj2 new StackOnly(obj1); // 报错operator new 和 拷贝构造 不能用了//StackOnly obj3(obj1); // 报错拷贝构造 不能用了//static StackOnly obj4(move(*obj1)); // 报错拷贝构造 和 移动构造 不能用了//static StackOnly obj2(obj1);return 0;
} 4.请设计一个类不能被继承
C98 方式
C98 中构造函数私有化派生类中调不到基类的构造函数则无法继承
class A
{
private:A() {};
};C11 方式
使用 final 关键字 final 关键字final修饰类表示该类不能被继承。
class A final
{
private:A() {};
};5.请设计一个类只能创建一个对象(单例模式)
5.1 设计模式
设计模式Design Pattern是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期七国之间经常打仗就发现打仗也是有套路的后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化设计模式是软件工程的基石脉络如同大厦的结构一样。
我们之前其实已经接触过一些设计模式了比如迭代器模式、适配器/配接器模式 下面我们要学习的是设计模式中的 单例模式
5.2 单例模式
一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式饿汉模式 和 懒汉模式
5.3 饿汉模式
就是说不管你将来用不用程序启动时就创建一个唯一的实例对象。即程序一开始就实例化一个该类对象给你了
为什么叫做饿汉饿汉就好比你放学饿着肚子回家在你回家前妈妈就已经准备好饭菜给你了
设计思路 1、构造函数私有化。 2、将 拷贝构造、移动构造、赋值重载 封死确保我们只有 GetInstance() 可以放出去唯一一个实例化对象。 3、创建一个自己这个类的静态成员对象一个类的静态成员只能创建一个而且static数据会在程序启动时创建好刚好符合 饿汉模式 的理念
// 单例模式一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。
class InfoMgr
{
public:// 只有这个函数可以向外提供唯一一个实例化对象static InfoMgr GetInstance() {return _ins;}void Print() {cout _ip \n;cout _port \n;cout _buffSize \n;}// 将 拷贝构造、移动构造、赋值重载 封死确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr) delete;InfoMgr(InfoMgr) delete;InfoMgr operator(const InfoMgr) delete;
private:// 将构造函数私有化外部无法直接构造该类对象InfoMgr() {cout InfoMgr() \n;}private:string _ip 127.0.0.1;int _port 80;size_t _buffSize 1024 * 1024;// 这不能说是在类中创建一个自己否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr _ins;
};
InfoMgr InfoMgr::_ins;int main() {// 调试程序可以发现调试还没有开始走就已经打印 InfoMgr() 说明 在main函数程序执行前对象就已经构造好了这是因为该对象是 static全局域InfoMgr::GetInstance().Print();return 0;
}饿汉模式的缺陷
如果这个单例对象在多线程高并发环境下频繁使用性能要求较高那么显然使用饿汉模式来避免资源竞争提高响应速度更好。
由于饿汉模式的对象在 main 函数前就被创建所以它不存在线程安全问题但是它也存在一些缺点
1、多个饿汉模式的单例某个对象初始化内容较多(读文件)会导致程序启动慢
2、A 和 B 两个饿汉对象初始化存在依赖关系要求A先初始化B再初始化饿汉无法保证其初始化顺序
5.4 懒汉模式
如果单例对象构造十分耗时或者占用很多资源比如加载插件啊 初始化网络连接啊读取文件啊等等而有可能该对象程序运行时不会用到那么也要在程序一开始就进行初始化就会导致程序启动时非常的缓慢 这些就是 饿汉模式的缺陷。 所以这种情况使用懒汉模式延迟加载更好。 饿汉模式的特点就是先创建好对象这也容易引发一些问题
懒汉模式 可以解决这个问题不先创建对象而是需要时再创建对象
这样就可以按需创建即你要吃的东西我不提前给你准备好只会在你需要吃时再做这就是懒汉
懒汉模式写法一定义类对象指针
在 main 函数中程序一般都会按顺序执行不像懒汉模式中全局变量执行顺序不定而且按需调用即可这样也可以解决 懒汉模式中的 依赖关系的先后问题
// 懒汉模式
class InfoMgr
{
public:// 若对象指针为 nullptr就给你 new 一个对象// 若不为空就返回该对象给你static InfoMgr GetInstance() {if (_pIns nullptr) {_pIns new InfoMgr();}return *_pIns;}void Print() {cout _ip \n;cout _port \n;cout _buffSize \n;}// 将 拷贝构造、移动构造、赋值重载 封死确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr) delete;InfoMgr(InfoMgr) delete;InfoMgr operator(const InfoMgr) delete;
private:// 将构造函数私有化外部无法直接构造该类对象InfoMgr() {cout InfoMgr() \n;}private:string _ip 127.0.0.1;int _port 80;size_t _buffSize 1024 * 1024;// 这不能说是在类中创建一个自己否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr* _pIns;
};
InfoMgr* InfoMgr::_pIns;int main() {// 调试程序可以发现类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}有没有发现 懒汉模式存在一个问题该模式中的对象是 new 出来的就需要手动 delete 释放
而我们上面的类中默认的析构只会将 _pIns 这个指针置空而不会 delete 指向的资源相当于 ”浅析构“会造成内存泄漏
实际上只有单例对象内存泄漏问题并没有这么严重
如果想要delete这里有个很好的方法定义内部类对象当本项目程序结束后该对象销毁会调用自己的析构函数我们就可以在析构函数里面设置 delete 相关程序
这其实是一种解决问题的 思想自己类无法做到的事可以定义内部类利用类的特性间接完成一些功能 // 定义一个内部类用于析构单例对象
class DestroyIns
{public:~DestroyIns() {if (InfoMgr::_pIns ! nullptr) {delete InfoMgr::_pIns;cout delete InfoMgr::_pIns; \n;}}
};InfoMgr::DestroyIns desIns; // 全局对象程序结束后会销毁自动调用析构函数则会执行析构函数里面 delete 的程序应用进去
// 懒汉模式
class InfoMgr
{
public:// 若对象指针为 nullptr就给你 new 一个对象// 若不为空就返回该对象给你static InfoMgr GetInstance() {if (_pIns nullptr) {_pIns new InfoMgr();}return *_pIns;}void Print() {cout _ip \n;cout _port \n;cout _buffSize \n;}// 将 拷贝构造、移动构造、赋值重载 封死确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr) delete;InfoMgr(InfoMgr) delete;InfoMgr operator(const InfoMgr) delete;// 定义一个内部类用于析构单例对象class DestroyIns{public:~DestroyIns() {if (InfoMgr::_pIns ! nullptr) {delete InfoMgr::_pIns;cout delete InfoMgr::_pIns; \n;}}};private:// 将构造函数私有化外部无法直接构造该类对象InfoMgr() {cout InfoMgr() \n;}private:string _ip 127.0.0.1;int _port 80;size_t _buffSize 1024 * 1024;// 这不能说是在类中创建一个自己否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr* _pIns;
};
InfoMgr* InfoMgr::_pIns;
InfoMgr::DestroyIns desIns;int main() {// 调试程序可以发现类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}懒汉模式二利用 局部静态变量推荐写这个 局部静态变量
函数内的静态变量也称为局部静态变量其作用域只限于函数内部别的函数不能访问。
局部静态变量存储在全局数据区只允许初始化一次但它的生命周期和全局变量一样自它们被定义时就一直存在直到程序结束时才会被销毁。不会随着函数的结束而被销毁会一直存在 特性只允许初始化一次
作用域在函数内部
存储区全局静态区
生命周期全局不会随着函数的结束而被销毁程序结束时才会被销毁 由于局部静态变量的特性也可以达到 第一次调用 GetInstance() 函数就定义一个类对象其他时候调用不会重新定义只允许定义一次 的目的
同时程序结束时会该对象也会自动销毁不用再定义内部类对齐处理了 这个写法简单明了相比前一种写法更加巧妙
// 懒汉模式二利用 局部静态变量
class InfoMgr
{
public:// 若对象指针为 nullptr就给你 new 一个对象// 若不为空就返回该对象给你static InfoMgr GetInstance() {static InfoMgr pIns;return pIns;}void Print() {cout _ip \n;cout _port \n;cout _buffSize \n;}// 将 拷贝构造、移动构造、赋值重载 封死确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr) delete;InfoMgr(InfoMgr) delete;InfoMgr operator(const InfoMgr) delete;private:// 将构造函数私有化外部无法直接构造该类对象InfoMgr() {cout InfoMgr() \n;}private:string _ip 127.0.0.1;int _port 80;size_t _buffSize 1024 * 1024;
};int main() {// 调试程序可以发现类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}