当前位置: 首页 > news >正文

天水网站建设博客怎么做wordpress主题

天水网站建设博客,怎么做wordpress主题,上海企业服务云代表啥,开封网站开发公司有时你想这样管理某些对象#xff0c;要让某种类型的对象能够自我销毁#xff0c;也就是能够“delete this”。很明显这种管理方式需要此类型对象被分配在堆中。而其它一些时候你想获得一种保障#xff1a;“不在堆中分配对象#xff0c;从而保证某种类型的类不会发生内存泄…有时你想这样管理某些对象要让某种类型的对象能够自我销毁也就是能够“delete this”。很明显这种管理方式需要此类型对象被分配在堆中。而其它一些时候你想获得一种保障“不在堆中分配对象从而保证某种类型的类不会发生内存泄漏。”如果你在嵌入式系统上工作就有可能遇到这种情况发生在嵌入式系统上的内存泄漏是极其严重的其堆空间是非常珍贵的。有没有可能编写出代码来要求或禁止在堆中产生对象heap-based object呢通常是可以的不过这种代码也会把“on the heap”的概念搞得比你脑海中所想的要模糊。 要求在堆中建立对象 让我们先从必须在堆中建立对象开始说起。为了执行这种限制你必须找到一种方法禁止以调用“new”以外的其它手段建立对象。这很容易做到。非堆对象non-heap object在定义它的地方被自动构造在生存时间结束时自动被释放所以只要禁止使用隐式的构造函数和析构函数就可以实现这种限制。 把这些调用变得不合法的一种最直接的方法是把构造函数和析构函数声明为private。这样做副作用太大。没有理由让这两个函数都是private。最好让析构函数成为private让构造函数成为public。处理过程与条款26相似你可以引进一个专用的伪析构函数用来访问真正的析构函数。客户端调用伪析构函数释放他们建立的对象。WQ加注注意异常处理体系要求所有在栈中的对象的析构函数必须申明为公有 例如如果我们想仅仅在堆中建立代表unlimited precision numbers无限精确度数字的对象可以这样做 class UPNumber { public: UPNumber(); UPNumber(int initValue); UPNumber(double initValue); UPNumber(const UPNumber rhs); // 伪析构函数 (一个const 成员函数 因为 // 即使是const对象也能被释放。) void destroy() const { delete this; } ... private: ~UPNumber(); }; 然后客户端这样进行程序设计 UPNumber n;                          // 错误! (在这里合法 但是 // 当它的析构函数被隐式地 // 调用时就不合法了) UPNumber *p new UPNumber;          //正确 ... delete p;                            // 错误! 试图调用 // private 析构函数 p-destroy();                        // 正确 另一种方法是把全部的构造函数都声明为private。这种方法的缺点是一个类经常有许多构造函数类的作者必须记住把它们都声明为private。否则如果这些函数就会由编译器生成构造函数包括拷贝构造函数也包括缺省构造函数编译器生成的函数总是public参见Effecitve C 条款45。因此仅仅声明析构函数为private是很简单的因为每个类只有一个析构函数。 通过限制访问一个类的析构函数或它的构造函数来阻止建立非堆对象但是在条款26已经说过这种方法也禁止了继承和包容containment class UPNumber { ... };              // 声明析构函数或构造函数 // 为private class NonNegativeUPNumber: public UPNumber { ... };           // 错误! 析构函数或 //构造函数不能编译 class Asset { private: UPNumber value; ...                                // 错误! 析构函数或 //构造函数不能编译 }; 这些困难不是不能克服的。通过把UPNumber的析构函数声明为protected同时它的构造函数还保持public就可以解决继承的问题需要包含UPNumber对象的类可以修改为包含指向UPNumber的指针 class UPNumber { ... };              // 声明析构函数为protected class NonNegativeUPNumber: public UPNumber { ... };           // 现在正确了; 派生类 // 能够访问 // protected 成员 class Asset { public: Asset(int initValue); ~Asset(); ... private: UPNumber *value; }; Asset::Asset(int initValue) : value(new UPNumber(initValue))      // 正确 { ... } Asset::~Asset() { value-destroy(); }                 // 也正确 判断一个对象是否在堆中 如果我们采取这种方法我们必须重新审视一下“在堆中”这句话的含义。上述粗略的类定义表明一个非堆的NonNegativeUPNumber对象是合法的 NonNegativeUPNumber n;                // 正确 那么现在NonNegativeUPNumber对象n中的UPNumber部分也不在堆中这样说对么答案要依据类的设计和实现的细节而定但是让我们假设这样说是不对的所有UPNumber对象 —即使是做为其它派生类的基类—也必须在堆中。我们如何能强制执行这种约束呢 没有简单的办法。UPNumber的构造函数不可能判断出它是否做为堆对象的基类而被调用。也就是说对于UPNumber的构造函数来说没有办法侦测到下面两种环境的区别 NonNegativeUPNumber *n1 new NonNegativeUPNumber;            // 在堆中 NonNegativeUPNumber n2;               //不再堆中 不过你可能不相信我。也许你想你能够在new操作符、operator new和new 操作符调用的构造函数的相互作用中玩些小把戏参见条款M8。可能你认为你比他们都聪明可以这样修改UPNumber如下所示 class UPNumber { public: // 如果建立一个非堆对象抛出一个异常 class HeapConstraintViolation {}; static void * operator new(size_t size); UPNumber(); ... private: static bool onTheHeap;                 //在构造函数内指示 // 对象是否被构造在 ...                                    // 堆上 }; // obligatory definition of class static bool UPNumber::onTheHeap false; void *UPNumber::operator new(size_t size) { onTheHeap true; return ::operator new(size); } UPNumber::UPNumber() { if (!onTheHeap) { throw HeapConstraintViolation(); } proceed with normal construction here; onTheHeap false;                    // 为下一个对象清除标记 } 如果不再深入研究下去就不会发现什么错误。这种方法利用了这样一个事实“当在堆上分配对象时会调用operator new来分配raw memory”operator new设置onTheHeap为true每个构造函数都会检测onTheHeap看对象的raw memory是否被operator new所分配。如果没有一个类型为HeapConstraintViolation的异常将被抛出。否则构造函数如通常那样继续运行当构造函数结束时onTheHeap被设置为false然后为构造下一个对象而重置到缺省值。 这是一个非常好的方法但是不能运行。请考虑一下这种可能的客户端代码 UPNumber *numberArray new UPNumber[100]; 第一个问题是为数组分配内存的是operator new[]而不是operator new不过倘若你的编译器支持它你能象编写operator new一样容易地编写operator new[]函数。更大的问题是numberArray有100个元素所以会调用100次构造函数。但是只有一次分配内存的调用所以100个构造函数中只有第一次调用构造函数前把onTheHeap设置为true。当调用第二个构造函数时会抛出一个异常你真倒霉。 即使不用数组bit-setting操作也会失败。考虑这条语句 UPNumber *pn new UPNumber(*new UPNumber); 这里我们在堆中建立两个UPNumber让pn指向其中一个对象这个对象用另一个对象的值进行初始化。这个代码有一个内存泄漏我们先忽略这个泄漏这有利于下面对这条表达式的测试执行它时会发生什么事情 new UPNumber(*new UPNumber) 它包含new 操作符的两次调用因此要调用两次operator new和调用两次UPNumber构造函数参见条款8。程序员一般期望这些函数以如下顺序执行 调用第一个对象的operator new 调用第一个对象的构造函数 调用第二个对象的operator new 调用第二个对象的构造函数 但是C语言没有保证这就是它调用的顺序。一些编译器以如下这种顺序生成函数调用 调用第一个对象的operator new 调用第二个对象的operator new 调用第一个对象的构造函数 调用第二个对象的构造函数 编译器生成这种代码丝毫没有错但是在operator new中set-a-bit的技巧无法与这种编译器一起使用。因为在第一步和第二步设置的bit第三步中被清除那么在第四步调用对象的构造函数时就会认为对象不再堆中即使它确实在。 这些困难没有否定让每个构造函数检测*this指针是否在堆中这个方法的核心思想它们只是表明检测在operator new(或operator new[])里的bit set不是一个可靠的判断方法。我们需要更好的方法进行判断。 如果你陷入了极度绝望当中你可能会沦落进不可移植的领域里。例如你决定利用一个在很多系统上存在的事实程序的地址空间被做为线性地址管理程序的栈从地址空间的顶部向下扩展堆则从底部向上扩展 在以这种方法管理程序内存的系统里很多系统都是但是也有很多不是这样你可能会想能够使用下面这个函数来判断某个特定的地址是否在堆中 // 不正确的尝试来判断一个地址是否在堆中 bool onHeap(const void *address) { char onTheStack;                   // 局部栈变量 return address onTheStack; } 这个函数背后的思想很有趣。在onHeap函数中onTheSatck是一个局部变量。因此它在堆栈上。当调用onHeap时它的栈框架stack frame(也就是它的activation record)被放在程序栈的顶端因为栈在结构上是向下扩展的趋向低地址onTheStack的地址肯定比任何栈中的变量或对象的地址小。如果参数address的地址小于onTheStack的地址它就不会在栈上而是肯定在堆上。 到目前为止这种逻辑很正确但是不够深入。最根本的问题是对象可以被分配在三个地方而不是两个。是的栈和堆能够容纳对象但是我们忘了静态对象。静态对象是那些在程序运行时仅能初始化一次的对象。静态对象不仅仅包括显示地声明为static的对象也包括在全局和命名空间里的对象参见条款47。这些对象肯定位于某些地方而这些地方既不是栈也不是堆。 它们的位置是依据系统而定的但是在很多栈和堆相向扩展的系统里它们位于堆的底端。先前内存管理的图片到讲述的是事实而且是很多系统都具有的事实但是没有告诉我们这些系统全部的事实加上静态变量后这幅图片如下所示 onHeap不能工作的原因立刻变得很清楚了不能辨别堆对象与静态对象的区别 void allocateSomeObjects() { char *pc new char;               // 堆对象: onHeap(pc) // 将返回true char c;                            // 栈对象: onHeap(c) // 将返回false static char sc;                    // 静态对象: onHeap(sc) // 将返回true ... } 现在你可能不顾一切地寻找区分堆对象与栈对象的方法在走头无路时你想在可移植性上打主意但是你会这么孤注一掷地进行一个不能获得正确结果的交易么绝对不会。我知道你会拒绝使用这种虽然诱人但是不可靠的“地址比对”技巧。 令人伤心的是不仅没有一种可移植的方法来判断对象是否在堆上而且连能在多数时间正常工作的“准可移植”的方法也没有。如果你实在非得必须判断一个地址是否在堆上你必须使用完全不可移植的方法其实现依赖于系统调用只能这样做了。因此你最好重新设计你的软件以便你可以不需要判断对象是否在堆中。 如果你发现自己实在为对象是否在堆中这个问题所困扰一个可能的原因是你想知道对象是否能在其上安全调用delete。这种删除经常采用“delete this”这种声明狼籍的形式。不过知道“是否能安全删除一个指针”与“只简单地知道一个指针是否指向堆中的事物”不一样因为不是所有在堆中的事物都能被安全地delete。再考虑包含UPNumber对象的Asset对象 class Asset { private: UPNumber value; ... }; Asset *pa new Asset; 很明显*pa包括它的成员value在堆上。同样很明显在指向pa-value上调用delete是不安全的因为该指针不是被new返回的。 幸运的是“判断是否能够删除一个指针”比“判断一个指针指向的事物是否在堆上”要容易。因为对于前者我们只需要一个operator new返回的地址集合。因为我们能自己编写operator new函数参见Effective C条款8—条款10所以构建这样一个集合很容易。如下所示我们这样解决这个问题 void *operator new(size_t size) { void *p getMemory(size);         //调用一些函数来分配内存 //处理内存不够的情况 把 p加入到一个被分配地址的集合; return p; } void operator delete(void *ptr) { releaseMemory(ptr);                // return memory to // free store 从被分配地址的集合中移去ptr; } bool isSafeToDelete(const void *address) { 返回address是否在被分配地址的集合中; } 这很简单operator new在地址分配集合里加入一个元素operator delete从集合中移去项目isSafeToDelete在集合中查找并确定某个地址是否在集合中。如果operator new 和 operator delete函数在全局作用域中它就能适用于所有的类型甚至是内建类型。 在实际当中有三种因素制约着对这种设计方式的使用。第一是我们极不愿意在全局域定义任何东西特别是那些已经具有某种含义的函数象operator new和operator delete。正如我们所知只有一个全局域只有一种具有正常特征形式也就是参数类型的operator new和operator delete条款9。这样做会使得我们的软件与其它也实现全局版本的operator new 和operator delete的软件例如许多面向对象数据库系统不兼容。 我们考虑的第二个因素是效率如果我们不需要这些为什么还要为跟踪返回的地址而负担额外的开销呢 最后一点可能有些平常但是很重要。实现isSafeToDelete让它总能够正常工作是不可能的。难点是多继承下来的类或继承自虚基类的类有多个地址所以无法保证传给isSafeToDelete的地址与operator new 返回的地址相同即使对象在堆中建立。有关细节参见条款M24和条款M31。 我们希望这些函数提供这些功能时能够不污染全局命名空间没有额外的开销没有正确性问题。幸运的是C使用一种抽象mixin基类满足了我们的需要。 抽象基类是不能被实例化的基类也就是至少具有一个纯虚函数的基类。mixin(“mix in”)类提供某一特定的功能并可以与其继承类提供的其它功能相兼容参见Effective C条款7。这种类几乎都是抽象类。因此我们能够使用抽象混合mixin基类给派生类提供判断指针指向的内存是否由operator new分配的能力。该类如下所示 class HeapTracked {                  // 混合类; 跟踪 public:                              // 从operator new返回的ptr class MissingAddress{};            // 异常类见下面代码 virtual ~HeapTracked() 0; static void *operator new(size_t size); static void operator delete(void *ptr); bool isOnHeap() const; private: typedef const void* RawAddress; static listRawAddress addresses; }; 这个类使用了list链表数据结构跟踪从operator new返回的所有指针list是标准C库的一部分参见Effective C条款49和本书条款35。operator new函数分配内存并把地址加入到list中operator delete用来释放内存并从list中移去地址元素。isOnHeap判断一个对象的地址是否在list中。 HeapTracked类的实现很简单调用全局的operator new和operator delete函数来完成内存的分配与释放list类里的函数进行插入操作和删除操作并进行单语句的查找操作。以下是HeapTracked的全部实现 // mandatory definition of static class member listRawAddress HeapTracked::addresses; // HeapTracked的析构函数是纯虚函数使得该类变为抽象类。 // (参见Effective C条款14). 然而析构函数必须被定义 //所以我们做了一个空定义。. HeapTracked::~HeapTracked() {} void * HeapTracked::operator new(size_t size) { void *memPtr ::operator new(size);  // 获得内存 addresses.push_front(memPtr);         // 把地址放到list的前端 return memPtr; } void HeapTracked::operator delete(void *ptr) { //得到一个 iterator用来识别list元素包含的ptr //有关细节参见条款35 listRawAddress::iterator it find(addresses.begin(), addresses.end(), ptr); if (it ! addresses.end()) {       // 如果发现一个元素 addresses.erase(it);             //则删除该元素 ::operator delete(ptr);          // 释放内存 } else {                           // 否则 throw MissingAddress();          // ptr就不是用operator new }                                  // 分配的所以抛出一个异常 } bool HeapTracked::isOnHeap() const { // 得到一个指针指向*this占据的内存空间的起始处 // 有关细节参见下面的讨论 const void *rawAddress dynamic_castconst void*(this); // 在operator new返回的地址list中查到指针 listRawAddress::iterator it find(addresses.begin(), addresses.end(), rawAddress); return it ! addresses.end();      // 返回it是否被找到 } 尽管你可能对list类和标准C库的其它部分不很熟悉代码还是很一目了然。条款M35将解释这里的每件东西不过代码里的注释已经能够解释这个例子是如何运行的。 只有一个地方可能让你感到困惑就是这个语句在isOnHeap函数中 const void *rawAddress dynamic_castconst void*(this); 我前面说过带有多继承或虚基类的对象会有几个地址这导致编写全局函数isSafeToDelete会很复杂。这个问题在isOnHeap中仍然会遇到但是因为isOnHeap仅仅用于HeapTracked对象中我们能使用dynamic_cast操作符参见条款M2的一种特殊的特性来消除这个问题。只需简单地放入dynamic_cast把一个指针dynamic_cast成void*类型或const void*或volatile void* 。。。。。生成的指针将指向“原指针指向对象内存”的开始处。但是dynamic_cast只能用于“指向至少具有一个虚拟函数的对象”的指针上。我们该死的isSafeToDelete函数可以用于指向任何类型的指针所以dynamic_cast也不能帮助它。isOnHeap更具有选择性它只能测试指向HeapTracked对象的指针所以能把this指针dynamic_cast成const void*变成一个指向当前对象起始地址的指针。如果HeapTracked::operator new为当前对象分配内存这个指针就是HeapTracked::operator new返回的指针。如果你的编译器支持dynamic_cast 操作符这个技巧是完全可移植的。 使用这个类即使是最初级的程序员也可以在类中加入跟踪堆中指针的功能。他们所需要做的就是让他们的类从HeapTracked继承下来。例如我们想判断Assert对象指针指向的是否是堆对象 class Asset: public HeapTracked { private: UPNumber value; ... }; 我们能够这样查询Assert*指针如下所示 void inventoryAsset(const Asset *ap) { if (ap-isOnHeap()) { ap is a heap-based asset — inventory it as such; } else { ap is a non-heap-based asset — record it that way; } } 象HeapTracked这样的混合类有一个缺点它不能用于内建类型因为象int和char这样的类型不能继承自其它类型。不过使用象HeapTracked的原因一般都是要判断是否可以调用“delete this”你不可能在内建类型上调用它因为内建类型没有this指针。 禁止堆对象 判断对象是否在堆中的测试到现在就结束了。与此相反的领域是“禁止在堆中建立对象”。通常对象的建立这样三种情况对象被直接实例化对象做为派生类的基类被实例化对象被嵌入到其它对象内。我们将按顺序地讨论它们。 禁止用户直接实例化对象很简单因为总是调用new来建立这种对象你能够禁止用户调用new。你不能影响new操作符的可用性这是内嵌于语言的但是你能够利用new操作符总是调用operator new函数这点参见条款M8来达到目的。你可以自己声明这个函数而且你可以把它声明为private。例如如果你想不想让用户在堆中建立UPNumber对象你可以这样编写 class UPNumber { private: static void *operator new(size_t size); static void operator delete(void *ptr); ... }; 现在用户仅仅可以做允许它们做的事情 UPNumber n1;                         // okay static UPNumber n2;                  // also okay UPNumber *p new UPNumber;          // error! attempt to call // private operator new 把operator new声明为private就足够了但是把operator new声明为private而把iperator delete声明为public这样做有些怪异所以除非有绝对需要的原因否则不要把它们分开声明最好在类的一个部分里声明它们。如果你也想禁止UPNumber堆对象数组可以把operator new[]和operator delete[]参见条款M8也声明为private。operator new和operator delete之间的联系比大多数人所想象的要强得多。有关它们之间关系的鲜为人知的一面可以参见我的文章counting objects里的sidebar部分。 有趣的是把operator new声明为private经常会阻碍UPNumber对象做为一个位于堆中的派生类对象的基类被实例化。因为operator new和operator delete是自动继承的如果operator new和operator delete没有在派生类中被声明为public进行改写overwrite它们就会继承基类中private的版本如下所示 class UPNumber { ... };             // 同上 class NonNegativeUPNumber:          //假设这个类 public UPNumber {                 //没有声明operator new ... }; NonNegativeUPNumber n1;             // 正确 static NonNegativeUPNumber n2;      // 也正确 NonNegativeUPNumber *p             // 错误! 试图调用 new NonNegativeUPNumber;          // private operator new 如果派生类声明它自己的operator new当在堆中分配派生对象时就会调用这个函数于是得另找一种不同的方法来防止UPNumber基类的分配问题。UPNumber的operator new是private这一点不会对包含UPNumber成员对象的对象的分配产生任何影响 class Asset { public: Asset(int initValue); ... private: UPNumber value; }; Asset *pa new Asset(100);          // 正确, 调用 // Asset::operator new 或 // ::operator new, 不是 // UPNumber::operator new 实际上我们又回到了这个问题上来即“如果UPNumber对象没有被构造在堆中我们想抛出一个异常”。当然这次的问题是“如果对象在堆中我们想抛出异常”。正像没有可移植的方法来判断地址是否在堆中一样也没有可移植的方法判断地址是否不在堆中所以我们很不走运不过这也丝毫不奇怪毕竟如果我们能辨别出某个地址在堆上我们也就能辨别出某个地址不在堆上。但是我们什么都不能辨别出来。
http://www.hkea.cn/news/14518206/

相关文章:

  • 招标网站平台有哪些网站seo优化费用
  • 网站可以做什么最快网站备案
  • 公主岭网站开发长沙抖音推广代运营公司
  • 国产手机做系统下载网站上海网络营销培训
  • 京东联盟网站建设电脑版高端网站建设方案报价
  • 申请个人手机网站空间知名的设计网站
  • 备案网站域名查询西安网上注册公司流程
  • 怎么做淘宝网站赚钱吗wordpress音频播放不了
  • 建一个门户网站多少钱网站备案和实名认证
  • .net做的大型网站吗迁移wordpress本地到服务器
  • 网站404页面源码wordpress最多文章
  • 凡科 建设淘宝客网站做网站应怎么缴税
  • 建网站和开发app哪个难湖南关键词排名推广
  • 手机创建个人网站 免费新媒体销售好做吗
  • 重庆网站seo多少钱中信建设网站
  • 免费网站建设平台 iis建设网站 翻译
  • 有人做家具网站中介吗外贸电商怎么做
  • 怎么注册一个网站百度推广让我先做虚拟网站后
  • 清风算法受影响的网站c 网站开发用的人多吗
  • 域名网站是什么做网站排在前十名要多少钱
  • 软件工程师招聘简章win7系统优化工具
  • 娄底市网站建设深圳市网络营销推广服务公司
  • android开发和网站开发万网怎样做网站调试
  • 营销型网站建设怎么做试看30秒做受小视频网站
  • 高端模板建站报价seo技术大师
  • 手机微信官方网站首页php做的购物网站系统下载
  • 南阳做网站seo的小公司做网站赚钱
  • 广西自治区住房和城乡建设厅网站wordpress 过滤插件下载
  • 职业技术学院网站建设项目相应式网站
  • 网站 框架网页建设百度网站制作推广