目录
1、全局作用域
2、类作用域
2.1、设计模式之Pimpl
2.2、单例模式的自动释放
2.2.0、检测内存泄漏的工具valgrind
2.2.1、可以使用友元形式进行设计
2.2.2、内部类加静态数据成员形式
2.2.3、atexit方式进行
2.2.4、pthread_once形式
作用域可以分为类作用域、类名的作用域以及对象的作用域几部分内容。在类中定义的成员变量和成员函数的作用域是整个类,这些名称只有在类中(包含类的定义部分和类外函数实现部分)是可见的,在类外是不可见的,因此,可以在不同类中使用相同的成员名。另外,类作用域意味着不能从外部直接访问类的任何成员,即使该成员的访问权限是public,也要通过对象名来调用,对于static成员函数,要指定类名来调用。
如果发生“屏蔽”现象,类成员的可见域将小于作用域,但此时可借助this指针或“类名::”形式指明所访问的是类成员,这有些类似于使用::访问全局变量。例如:#include <iostream> using std::cout; using std::endl; int num = 1; namespace wd {int num = 20;class Example{public:void print(int num) const{cout << "形参num = " << num << endl;cout << "数据成员num = " << this->num << endl;cout << "数据成员num = " << Example::num << endl;cout << "命名空间中num = " << wd::num << endl;cout << "全局变量num = " << ::num << endl;}private:int num;}; }//end of namespace wdint main() {wd::Example().print(100); //通过匿名对象调用print函数return 0; }
和函数一样,类的定义没有生存期的概念,但类定义有作用域和可见域。使用类名创建对象时,首要的前提是类名可见,类名是否可见取决于类定义的可见域,该可见域同样包含在其作用域中,类本身可被定义在3种作用域内,这也是类定义的作用域。
1、全局作用域
在函数和其他类定义的外部定义的类称为全局类,绝大多数的 C++ 类是定义在该作用域中,我们在前面定义的所有类都是在全局作用域中,全局类具有全局作用域。
2、类作用域
一个类可以定义在另一类的定义中,这是所谓嵌套类或者内部类,举例来说,如果类A定义在类B中,如果A的访问权限是public,则A的作用域可认为和B的作用域相同,不同之处在于必须使用B::A的形式访问A的类名。当然,如果A的访问权限是private,则只能在类内使用类名创建该类的对象,无法在外部创建A类的对象。
#include<iostream> using namespace std; class Line { public:Line(int x1, int y1, int x2, int y2);void printLine() const;private:class Point{public:Point(int x = 0, int y = 0): _x(x), _y(y){}void print() const;private:int _x;int _y;}; private:Point _pt1;Point _pt2; }; Line::Line(int x1, int y1, int x2, int y2): _pt1(x1, y1), _pt2(x2, y2) {} void Line::printLine() const {_pt1.print();cout << " ---> ";_pt2.print();cout << endl; } void Line::Point::print() const {cout << "(" << _x<< "," << _y<< ")"; } int main() {Line l1(1, 2, 3, 4);l1.printLine();return 0; }
注意:由于Point是私有类,不能在类外对其进行访问:
如果是正确定义的:
2.1、设计模式之Pimpl
PImpl是Pointer to Implementation的缩写,也被称为“编译期实现”,是一种C++设计的模式。 用于将类的实现细节与其公共接口分离开来。该模式的核心思想是 通过一个指向类的实现的指针来隐藏类的实现细节,从而提高类的封装性和安全性。
PImpl是一种C++编程技巧,它将类的实现细节从对象表示中移除,放到一个分离的类中,并以一个不透明的指针进行访问。 此技巧用于构造拥有稳定 ABI 的 C++ 库接口,及减少编译时依赖。
一.PImpl的好处
使用PImpl模式的好处是:可以避免对实现细节的公开,从而减少了头文件中的依赖项和编译时间,并且使得类的实现可以更加灵活和方便地修改,而不会影响其公共接口。
在使用PImpl模式时,通常需要将类的实现细节封装在一个单独的结构体或类中,称为“实现类”或“pImpl类”,然后通过一个指向该实现类的指针来访问实现细节。这个指针通常作为 类的私有成员变量,并在类的构造函数和析构函数中进行初始化和清理。这样,当类的实现细节发生变化时,只需要修改实现类而不需要修改公共接口,从而实现了类的高内聚低耦合的设计目标。
参考博客:
设计模式之Pimpl模式-CSDN博客https://blog.csdn.net/xhtchina/article/details/112795569?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170865960916800227465939%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170865960916800227465939&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-112795569-null-null.nonecase&utm_term=%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8BPimpl&spm=1018.2226.3001.4450
2.2、单例模式的自动释放
在类和对象那一章,我们看过单例模式,其中对象是由_pInstance指针来保存的,而在使用单例设计模式的过程中,也难免会遇到内存泄漏的问题。那么是否有一个方法,可以让对象自动释放,而不需要程序员自己手动去释放呢?在学习了嵌套类之后,我们就可以完美的解决这一问题。
在涉及到自动的问题时,我们很自然的可以想到:当对象被销毁时,会自动调用其析构函数。利用这一特性,我们可以解决这一问题。
2.2.0、检测内存泄漏的工具valgrind
" 安装方式: $ sudo apt install valgrind " 使用方式: $ valgrind --tool=memcheck --leak-check=full ./test
2.2.1、可以使用友元形式进行设计
//1、友元实现单例对象的自动释放 class AutoRelease; class Singleton {friend AutoRelease; public:static Singleton *getInstance() {if(nullptr == _pInstance){_pInstance = new Singleton();}return _pInstance;}static void destroy(){if(_pInstance){delete _pInstance; //1、调用析构函数 2、operator delete_pInstance = nullptr;}} private:Singleton(){cout << "Singleton()" << endl;}~Singleton(){cout << "~Singleton()" << endl;} private:static Singleton *_pInstance; }; Singleton *Singleton::_pInstance = nullptr; class AutoRelease { public:AutoRelease(){cout << "AutoRelease()" << endl;}~AutoRelease(){cout << "~AutoRelease()" << endl;if(Singleton::_pInstance){delete Singleton::_pInstance;//1、调用析构函数 2、operator deleteSingleton::_pInstance = nullptr;}} };
2.2.2、内部类加静态数据成员形式
class Singleton { public:static Singleton * getInstance(){if(_pInstance == nullptr){_pInstance = new Singleton();}return _pInstance;}private:class AutoRelease{public:AutoRelease(){cout << "AutoRelease()" << endl;}~AutoReleas(){cout << "~AutoRelease()" << endl;if(_pInstance){delete _pInstance;_pInstance = nullptr;}}}; private:Singleton(){cout << "Singleton()" << endl;}~Singleton(){cout << "~Singleton()" << endl;}private:static Singleton *_pInstance;static AutoRelease _auto; };
2.2.3、atexit方式进行
class Singleton { public:static Singleton *getInstance(){//对于多线程环境,不安全if(nullptr == _pInstance){_pInstance = new Singleton();atexit(destroy);}return _pInstance;}static void destroy(){if(_pInstance){delete _pInstance;//1、调用析构函数 2、operator delete_pInstance = nullptr;}} private:Singleton(){cout << "Singleton()" << endl;}~Singleton(){cout << "~Singleton()" << endl;} private:static Singleton *_pInstance; }; /* Singleton *Singleton::_pInstance = nullptr; //饱汉模式(懒汉模式)*/ Singleton *Singleton::_pInstance = getInstance();//饿汉模式
2.2.4、pthread_once形式
//4、pthread_once,平台相关性的函数 class Singleton { public:static Singleton *getInstance(){pthread_once(&_once, init);return _pInstance; }static void init(){_pInstance = new Singleton();atexit(destroy);}static void destroy(){if(_pInstance){delete _pInstance;//1、调用析构函数 2、operator delete_pInstance = nullptr;}} private:Singleton(){cout << "Singleton()" << endl;}~Singleton(){cout << "~Singleton()" << endl;} private:static Singleton *_pInstance;static pthread_once_t _once; }; Singleton *Singleton::_pInstance = nullptr; //饱汉模式(懒汉模式) /* Singleton *Singleton::_pInstance = getInstance();//饿汉模式 */ pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT;