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

温江建设局备案网站wordpress内存优化

温江建设局备案网站,wordpress内存优化,黄山网站建设有哪些,大型的营销型网站建设TODO#xff1a;还没看太懂的篇章 item25 item35 模板相关内容 文章目录 基础视C为一个语言联邦以const, enum, inline替换#define尽可能使用constconst成员函数 确定对象使用前已被初始化 构造、析构和赋值内含引用或常量成员的类的赋值操作需要自己重写不想使用自动生成的函…TODO还没看太懂的篇章 item25 item35 模板相关内容 文章目录 基础视C为一个语言联邦以const, enum, inline替换#define尽可能使用constconst成员函数 确定对象使用前已被初始化 构造、析构和赋值内含引用或常量成员的类的赋值操作需要自己重写不想使用自动生成的函数时应主动拒绝为多态基类声明虚析构函数别让异常逃离析构函数不要在构造和析构时调用虚函数令operator返回一个this引用在operator中处理自我赋值修改类成员时记得修改其构造函数、拷贝函数等 资源管理用对象管理资源在资源管理类中小心复制行为在资源管理类中提供对原始资源的访问以独立语句将new的对象放入智能指针了解new-handler重载new和delete使用new和delete的对应形式 设计与声明让接口容易被使用设计类时多考虑一些尽量引用传递而非值传递必须值传递时就别引用传递了将成员变量声明为private用非成员且非友元函数替换成员函数若所有参数都要类型转换则为其采用非成员函数考虑写出一个不抛异常的swap函数 实现尽可能延后变量的定义尽量少转型避免返回指向private的handle指针或引用时刻考虑异常安全了解inline尽量降低文件间的编译依赖关系 面向对象Cpublic继承表示is-a关系复合表示has-a或“根据某物实现出”关系private继承表示“根据某物实现出”关系避免继承带来的重名问题itrem34 区分接口继承和实现继承考虑虚函数以外的其他选择绝不重新定义继承而来的默认参数谨慎使用多重继承 模板typename还用来标识嵌套从属类型需要类型转换时为模板定义非成员函数 基础 视C为一个语言联邦 可以将C视为以下4种次语言的结合体 C面向对象模板STL 每个次语言都有自己的规范因此当从其中一个切换到另一个时一些习惯或守则是可能会发生变化的。 以const, enum, inline替换#define 用const替换#define有以下2个原因 #define定义的符号名称可能没有进入符号表而只是简单地被替换因此在得到编译错误信息时不易改错。 例如#define PI 3.14在报错时可能给出3.14而不是PI。预处理器会盲目地做替换例如多次使用PI时就会出现多份3.14而使用const常量则可以只维护一个常量。 当不想让别人通过一个指针或引用来指向某个int常量时会用到enum hack做法即用enum来代替constint的使用。例如取一个const地址是合法的但取一个enum地址是非法的。 最后对于形似函数的宏最好改用inline。 尽可能使用const 只要某值是保持不变的就应该将它说出来。 const出现在*左边表示被指物是常量在*右边表示指针是常量。 但特殊的迭代器并非如此。对于迭代器 const vectorint::iterator iter // iter类似于T* const vectorint::const_iterator citer // citer类似于const T*对于函数令函数返回一个const可以预防某些无意义或不消息的错误例如if (a * b c)当abc是自定义类型时这句话不会报错但是显然我们要的是。 const成员函数 两个成员函数如果只是常量性constness即函数中行为是否是不变的不同则它们是可以被重载的。关于常量性有两个概念bitwise/physical constness和logical constness。 前者指const函数中不更改任何对象的任何一个bit这也是编译器的策略。 但bitwise constness会有不在预期的情况。例如在函数中有一个指针更改其所指物的值这并不违反bitwise constness但逻辑上不符合我们的预期。 因此就有了logical constness。它允许在函数中做一些修改只要逻辑上不能变的不变就行这是通过关键字mutable对变量进行修饰实现的。mutable释放掉非静态成员变量的bitwise constness约束使其可以在const函数中改变。 虽然编译器采用bitwise constness策略但写代码时应该使用logical constness。 另外一些const函数和非const函数有着相同的实现为了代码复用我们可以令非const函数调用它对应的const函数例如 class TextBlock { public:const char operator[](size_t position) const {// 做一些事情这部分const和非const函数都要做即是他们俩的共同实现return text[position];}char operator[](size_t position) {return const_castchar(static_castconst TextBlock(*this)[position]);} };这里做的首先是将原始的TextBlock类型转化为const TextBlock进行[]操作即调用const版本然后使用const_cast从返回值中移除const。 注意不能相反地用const版本调用非const版本。因为const函数是一种约束让它调用没有约束的函数就是违反了不改变的承诺。 确定对象使用前已被初始化 在构造函数中使用初始化列表比赋值效率更高因为后者会先调用无参默认构造函数然后再给成员变量赋值。总是使用初始化列表是没有问题的。 C有十分固定的成员初始化顺序基类早于派生类成员变量总是以声明的顺序被初始化。 另外为避免多个编译单元之间的非局部静态对象初始化次序问题应使用一个函数返回指向静态对象的引用而非直接使用静态对象自身这其实就是单例模式的一个常见手法。 所谓静态对象就是指从被构造一直存在到程序结束的对象。静态对象包括全局对象、定义于命名空间作用域内的对象以及在类内、函数内核在文件作用域内被声明为static的对象。其中函数内的static对象被称为局部静态对象其他则是非局部静态对象。 构造、析构和赋值 内含引用或常量成员的类的赋值操作需要自己重写 不想使用自动生成的函数时应主动拒绝 如果不想编译器自动生成拷贝函数等可以将其声明为private并且不去实现它这样的话如果别人试图调用则会得到一个链接错误。 当然也可以将这个链接错误提前到编译期做法是设计一个阻止自动生成该函数的基类例如不想要自动生成拷贝构造函数和赋值操作符可以这样实现 class Uncopyable { protected:Uncopyable() {}~Uncopyable() {} private:// 阻止拷贝构造和赋值操作符Uncopyable(const Uncopyable);Uncopyable operator(const Uncopyable); };// 然后将自己的类继承自Uncopyable即可 class MyClass : private Uncopyable { };为多态基类声明虚析构函数 如果不这么做在用基类指针析构派生类对象时可能会造成局部销毁即只析构了基类的部分。 当希望拥有一个抽象类但并无可用的纯虚函数时就可以为它声明一个纯虚析构函数并且必须为它提供一个实现哪怕为空。 注意给基类声明虚析构函数这个规则只适用于具有多态性质的基类目的是为了通过基类接口处理派生类对象。如果不是具备多态性则不用声明虚析构函数。 别让异常逃离析构函数 析构函数绝对不要吐出而应该捕获任何异常然后吞下不处理或结束程序。 还有一个策略是把可能导致异常的函数不止在析构函数调用而且提供一个它的调用接口这样就给了客户一个处理该异常的机会。 不要在构造和析构时调用虚函数 简单地说在基类构造期间虚函数还不是虚函数。或者根本地讲在派生类对象的基类构造期间对象类型是基类而非派生类。 令operator返回一个this引用 所有的、等操作符都应该return *this。 在operator中处理自我赋值 形如x x这样的自我赋值当然不易发生但由于基类的指针或引用可以指向其派生类对象这就是潜在的自我赋值情况。 自我赋值可能引发一些问题例如下面这种情况在自我赋值时会由于先被delete掉而出错 class MyClass { // private:SomeClass* sp; };MyClass Myclass::operator(const MyClass rhs) { // rhs指right hand side即右边的值delete sp;sp new SomeClass(rhs.sp);return *this; }要阻止这种错误可以在函数最前面做一个identify test来检验 MyClass Myclass::operator(const MyClass rhs) {// identify testif (this rhs)// 如果是自我赋值则什么都不做return *this;delete sp;sp new SomeClass(rhs.sp);return *this; }除了自我赋值安全性还要注意异常安全性例如上例中new那句可能抛出异常但此时指针已经被delete。 令人高兴的是让operator具备异常安全性的同时往往会自动地获得自我赋值安全性例如这样写 MyClass Myclass::operator(const MyClass rhs) {SomeClass* temp sp;sp new SomeClass(rhs.sp);delete sp;return *this; }修改类成员时记得修改其构造函数、拷贝函数等 另外与非const函数调用const函数来复用代码相比不要令拷贝赋值操作符调用拷贝构造函数因为这就像构造一个已存在的对象。 如果要复用代码可以提取共同部分创建一个新的函数供它们调用这个函数往往是private的而且命名为init()。 资源管理 用对象管理资源 首先明确资源指的就是一旦使用将来必须还给系统的东西把资源放到一个管理资源的对象内便可以依赖析构函数自动调用的机制确保其释放。 这个观念也叫作资源获取即初始化Rescource Acquisition Is Initialization, RAII可以使用智能指针或共享指针来在资源初始化的时候就获取它来实现该功能也可以自己实现一个资源管理类。 在资源管理类中小心复制行为 智能指针在使用拷贝构造函数或赋值操作符时会将之前的智能指针变为null而复制所得的指针将获得资源的唯一拥有权以避免将来对同一资源的多次delete。共享指针则没有该特性而是一种引用计数型智能指针reference-counting smart pointer, RCSP它在引用数为0时删除所指物如果我们想要在引用为0时不删除而是做些其他事情可以自定义一个删除器deleter函数替代其行为。 另外注意复制资源管理对象时应该同时复制其拥有的资源即进行深拷贝。 在资源管理类中提供对原始资源的访问 对于原始资源的访问可以采用显式的或隐式的转换方法。 auto_ptr和trl::shared_ptr都提供了一个get()显式地访问原始资源也可以隐式地转换函数即使用-或*操作符。 RAII类返回原始资源是否违背了封装呢是的但并无大碍因为它本来就不是为了封装而存在的一个类它只是为了确保资源的释放而已。 以独立语句将new的对象放入智能指针 我认为这条建议更准确地说应该是RAII类在初始化并获取资源时应该是一句独立语句。 例如在调用funcA(std::auto_ptrSomeClass(new SomeClass), funcB())这样的函数前编译器会做以下三件事 调用funcB()执行new SomeClass调用std::auto_ptr构造函数 但C并不保证这些事情的顺序如果以这样的顺序执行 执行new SomeClass调用funcB()调用std::auto_ptr构造函数 并且在调用funcB()时触发了异常那么RAII的初始化new SomeClass和获取用auto_ptr获得资源就被分开了此时new返回的指针就遗失了。 避免这类问题的方法就是将RAII作为一句独立语句 std::auto_ptrSomeClass p(new SomeClass); funcA(p, funcB());了解new-handler 当new抛出异常前会调用一个客户指定的错误处理程序即所谓的new-handler。 我们重载new时必须用std::set_new_handler(someFunc)来指定new-handler。一个设计良好的new-handler必须做到让更多的内存可被使用或设置另一个new-handler。new-handler要一直设置并调用下一个new-handler直到足够的内存被释放出来或传给它null指针并抛出异常当然也可以在中间终止程序。 重载new和delete 什么时候需要重载new和delete呢 需要检测运行上的错误 例如在每次分配内存时在首或尾多分配几个额外的字节即签名delete时则可以检测其值是否不变以检测是否发生了错误类似于栈保护中的canary值。需要加强效率 默认的new和delete的分配策略没有针对性如果我们确定性能瓶颈来源于内存分配策略那么就需要定制new和delete以提升性能。需要收集分配时的信息 如果基类重载的new不想用来new派生类对象多数情况也不应如此它们的大小是不同的可以这样来写 void* Base::operator new(std::size_t size) {if (size ! sizeof(Base))// 用标准的new处理return ::operator new(size); }使用new和delete的对应形式 在new数组时也一定要使用delete[]释放一般情况当然不会犯这种错误但下面这种情况就容易出错 typedef std::string Lines[4]; // Lines的4行中每一行都是一个string std::string* lp new Lines; // 返回一个string*就像new string[4]一样 // 此时应该 delete[] lp;为了避免这类错误尽量不要对数组形式做typedef。 另外考虑SomeClass* p new SomeClass这里调用了两个函数分配内存的new和构造函数。如果new调用成功而构造函数抛出异常就可能会造成内存泄漏。为了解决这个问题运行期系统此时会调用这个new对应的delete但如果我们重载了new而没有重载对应的delete就真的会发生内存泄漏。 同样的情景如果重载了placement new就一定要写placement delete。实际上placement delete只有在“其对应的new触发的构造函数出现异常”时才会被调用而简单地对一个指针使用delete是永远不会调用placement delete的。因此如果要避免placement new相关的内存泄漏我们需要提供一个正常的delete用于构造期间无异常和一个对应的placement delete用于异常的构造期间。 设计与声明 让接口容易被使用 保证类的约束和一致性等例如Month类只能有1~12月可以这样实现 class Month { public:// 以函数替代对象static Month Jan() {return Month(1);}static Month Feb() {return Month(2);}// ··· private:explicit Month(int m); // 阻止生成新的月份 };另外万一客户忘记使用智能指针怎么办较好的办法是先发制人地让工厂函数返回智能指针而不是对象 SomeClass* createClass(); // 客户可能不使用智能指针 std::auto_ptrSomeClass createClass(); // 强迫客户使用智能指针设计类时多考虑一些 对象如何被创建和销毁 涉及构造、析构、new和delete的设计。对象如何被值传递 拷贝构造函数用于定义值传递的实现。对象的初始化和赋值 决定了构造函数和operator的设计和不同。对象的合法值 进行约束、检查和异常抛出。它的继承图系 需要虚函数等。对象需要怎样的类型转换 编写需要的类型转换函数。需要重载哪些操作符什么样的行为需要封装 或者什么样的标准函数需要被声明为private以避免调用、谁会用到该对象 决定了什么是public什么是protected什么是friends。对象的未声明接口是什么 它对效率、异常安全性和资源运用提供何种保证 尽量引用传递而非值传递 前者往往效率更高且可避免切割问题slicing problem即派生类对象采用值传递的方式并被视为了一个基类对象会调用基类的构造函数进而造成构造不完全。 注意这条并不适用于内置类型和STL的迭代器和函数对象。 必须值传递时就别引用传递了 如果是栈对象就不要用指针或引用返回如果是堆对象就不要用引用返回如果是局部静态对象且有可能同时需要多个这样的对象就不要用指针或引用返回。 将成员变量声明为private 一旦一个成员变量被声明为public或protected且客户开始使用它就很难改变那个变量所涉及的一切因为需要太多的重写、测试、编译等环节了。 越多的东西被封装我们改变它们的权利就越大。 用非成员且非友元函数替换成员函数 有一个反直觉的现象非成员且非友元函数的封装性是要优于成员函数的。 因为成员函数还可以访问它用不到的private变量或函数等但非成员且非友元函数则不行它只能做自己该做的事情。 比较自然的做法是声明这样一个非成员非友元函数将其放到和类所在的同一个命名空间内。 若所有参数都要类型转换则为其采用非成员函数 一个所有参数都要类型转换的例子就是比如有理数类Rational重载了*操作符就可以Rational r2 r1 * someInt但不能Rational r2 someInt * r1因为后者调用的是int的*操作符。 此时就应该将重载*操作符作为一个非成员函数。 那么是否需要将其声明为友元函数呢答案是不用因为*操作符并不需要用到Rational类的私有变量。注意如果可以尽量避免友元函数。 本条款只适合C的面向对象次语言而不适合模板次语言。 考虑写出一个不抛异常的swap函数 copy and swap原则为打算修改的对象做一个副本在副本上做一切修改如果有问题则抛出异常此时不影响之前的对象因此异常安全。修改完后在一个不抛出异常的操作中交换原对象和副本。 还有很多看不懂学一些模板后再看吧。 实现 尽可能延后变量的定义 不止应该延后变量的声明到使用为止还应该延后到给它赋初值为止。 如果是循环内的变量则要综合考虑它的构造、析构、赋值代价以确定是在循环外还是循环内声明。 尽量少转型 C提供四种新的转型方式 const_cast 将对象的常量性移除cast away the constness。dynamic_cast 安全向下转型用于决定某对象是否归属继承体系中的某类型。通常用于当要在派生类对象上执行派生类函数但手上只有一个基类指针时。 dynamic_cast通常效率很低谨慎使用。reinterpret_cast 执行低级转型实际动作取决于编译器。static_cast 强行隐式转换。 另外注意C中单一对象可能拥有一个以上的地址行为如下 Derived d; // 这句隐式地将Derived*转换为Base* Base* pb d;避免返回指向private的handle指针或引用 一是容易引起bitwise constness导致的错误。 二是不论这个handle是个指针、引用或迭代器还是说它是个常量或返回它的函数是个常量函数关键是一个handle被传出去了这就可能造成handle比起所指对象更长寿的风险。 时刻考虑异常安全 时刻想想某函数抛出异常会怎样它后面的语句本该执行而没有执行会出问题吗 关于异常安全有三个等级的保证 基本承诺 如果有异常被抛出承诺程序内的任何事物都仍保持在有效状态下没有任何对象或数据被破坏。强烈保证 如果有异常被抛出保证程序状态不变即函数如果失败程序会回到调用函数之前的状态。保证不抛出 保证不抛出异常。 强烈保证往往能以copy and swap实现但强烈保证并不总是有效它需要函数中的所有部分都是强烈保证才行。 了解inline inline导致的代码膨胀可能导致额外的换页行为因而降低cache的命中率进而影响效率。 尽量降低文件间的编译依赖关系 尝试接口与实现分离关键在于以声明的依赖性替换定义的依赖性。常用的方法是handle class和接口类。 记住如果对象引用或指针可以完成定义就不要用对象否则还会引出该类型的定义式。并且在要用到其他类时尽量以它的声明式替换其定义式。 例如把一个Person类分割为两个类分别是接口类和实现类。接口类只有一个指针成员指向其实现类这种设计被称为pimpl(pointer to implementation) idiom而这种类就被称为handle class。 另一种制作handle class的方法是令Person称为一个抽象基类即接口类这种类的目的是详细地一一描述其派生类的接口。它的派生类则需要有办法为它创建对象这就需要一个特殊的函数扮演派生类的构造函数这就是工厂函数。 面向对象C public继承表示is-a关系 public继承是一种is-a关系is-a指的是适用于基类的每一件事情都适用于派生类。但有时常识中的is-a关系在面向对象中并不成立例如鸟会飞企鹅继承鸟但企鹅不会飞。因此类设计需要更多的考虑这些情况但不需要超出软件需求的考虑如果需求中用不到企鹅飞的部分就不需要考虑。 复合表示has-a或“根据某物实现出”关系 程序中的对象可以分为应用域和实现域前者如人、汽车等后者如缓冲区、锁、查找树等。 当复合在应用域时表示has-a关系在实现域时则表示is-implemented-in-terms-of根据某物实现出关系。 private继承表示“根据某物实现出”关系 private继承是一种实现技术意为派生类是根据基类实现的这也是为什么继承而来的所有东西都变成private的原因因为它们都是实现的细枝末节因此private继承在设计上没有意义只在软件实现层面有意义。 复合也可以表示“根据某物实现出”的关系要尽可能使用复合除非当派生类需要访问基类的protected成员或需要重新定义继承而来的虚函数时才用private继承原因有二 当一个类需要private继承一个实现但并不想要自己的派生类改变其实现时就需要使用复合而非private继承。这相当于Java中的final关键字即阻止派生类重新定义虚函数。可以降低编译依赖性。 另外有一种情况是需要使用private继承的即空基类最优化empty base optimization, EBO此时使用private继承可以节约空间。 所谓的空类empty class指不使用空间的类但由于C的规定空类至少要被安插一个char的大小到空对象内因此对于下面这种情况sizeof(SomeClass) sizeof(int)。 class Empty{}; class SomeClass { private:int x;Empty e; };此时使用private继承就可以节约空间使得sizeof(SomeClass) sizeof(int)。 class SomeClass : private Empty { private:int x; };现实中的空类并不真正的空它虽然没有非静态成员但往往有typedef, enum, static或非虚函数。 另外补充如果继承类型是private编译器就不会自动将派生类对象转换成基类对象。 避免继承带来的重名问题 如果真的有重名可以使用using来得到被遮盖的变量。 itrem34 区分接口继承和实现继承 纯虚函数是为了让派生类只继承函数接口。 它要求“必须提供一个该函数但不干涉你怎么实现”。普通虚函数是为了让派生类继承函数接口和缺省实现。 它要求“必须提供一个该函数但如果你不想写可以用我的缺省版本”。非虚函数则代表其一般性高于特殊性因此绝不该在派生类中被重新定义。 非虚函数是静态绑定的。任何一个派生类对象都有可能表现出基类或派生类的行为这取决于指向该对象的指针在声明时的类型是基类指针还是派生类指针。 文中还提到可以利用纯虚函数“也可以先实现且必须在派生类中重新声明”的特点避免派生类忘记实现虚函数而非预期地使用其缺省版本。具体做法是将原本的虚函数改为纯虚函数并实现它然后在派生类要用到缺省版本时手动调用之前实现的纯虚函数这样在本应实现却忘记实现该虚函数时就会引发报错。 但是和这样麻烦的做法相比我觉得“记得实现该函数”不应该才是更应该做的吗 考虑虚函数以外的其他选择 绝不重新定义继承而来的默认参数 一个例子 class Shape { public:enum Color {Red, Green, Blue};// 每个形状都要有draw函数默认为红色virtual void draw(Color color Red) const 0; };class Rectangle : public Shape { public:virtual void draw(Color color Blue) const;/** 大错误虚函数是动态绑定而默认参数是静态绑定* 如果有Shape* pr new Rectangle;* 因为pr动态类型是Rectangle*因此调用的是Rectangle的draw()* 而其静态类型却是Shape*因此默认参数是来自Shape的Red*/ };class Circle : public Shape { public:virtual void draw(Color color) const;/* * 注意虽然基类有默认参数* 但用对象调用该函数时还是一定要指定参数* 因为静态绑定下该函数并不从基类继承默认参数* 但若用指针或引用调用该函数则可以获得默认参数* 因为动态绑定下会得到继承*/ };当想要类似上面的虚函数表现出期望的行为时应该考虑替换设计。例如让非虚函数指定默认参数并调用一个private虚函数来完成真正的工作 class Shape { public:enum Color {Red, Green, Blue};// 只用来指定默认参数void draw(Color color Red) const {doDraw(color);} private:// 真正完成工作的函数virtual void doDraw(Color color) const 0; };class Rectangle : public Shape { private:virtual void doDraw(Color color) const; }注意继承而来的private的虚函数是可以重写的。 谨慎使用多重继承 多重继承可能引发钻石型继承这可能会造成派生类从多个路径继承重复的成员例如: #mermaid-svg-oW2i9veJouiGo4tL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-oW2i9veJouiGo4tL .error-icon{fill:#552222;}#mermaid-svg-oW2i9veJouiGo4tL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oW2i9veJouiGo4tL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-oW2i9veJouiGo4tL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oW2i9veJouiGo4tL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oW2i9veJouiGo4tL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oW2i9veJouiGo4tL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oW2i9veJouiGo4tL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oW2i9veJouiGo4tL .marker.cross{stroke:#333333;}#mermaid-svg-oW2i9veJouiGo4tL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oW2i9veJouiGo4tL g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-oW2i9veJouiGo4tL g.classGroup text .title{font-weight:bolder;}#mermaid-svg-oW2i9veJouiGo4tL .nodeLabel,#mermaid-svg-oW2i9veJouiGo4tL .edgeLabel{color:#131300;}#mermaid-svg-oW2i9veJouiGo4tL .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-oW2i9veJouiGo4tL .label text{fill:#131300;}#mermaid-svg-oW2i9veJouiGo4tL .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-oW2i9veJouiGo4tL .classTitle{font-weight:bolder;}#mermaid-svg-oW2i9veJouiGo4tL .node rect,#mermaid-svg-oW2i9veJouiGo4tL .node circle,#mermaid-svg-oW2i9veJouiGo4tL .node ellipse,#mermaid-svg-oW2i9veJouiGo4tL .node polygon,#mermaid-svg-oW2i9veJouiGo4tL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oW2i9veJouiGo4tL .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-oW2i9veJouiGo4tL g.clickable{cursor:pointer;}#mermaid-svg-oW2i9veJouiGo4tL g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-oW2i9veJouiGo4tL g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-oW2i9veJouiGo4tL .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-oW2i9veJouiGo4tL .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-oW2i9veJouiGo4tL .dashed-line{stroke-dasharray:3;}#mermaid-svg-oW2i9veJouiGo4tL #compositionStart,#mermaid-svg-oW2i9veJouiGo4tL .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #compositionEnd,#mermaid-svg-oW2i9veJouiGo4tL .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #dependencyStart,#mermaid-svg-oW2i9veJouiGo4tL .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #dependencyStart,#mermaid-svg-oW2i9veJouiGo4tL .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #extensionStart,#mermaid-svg-oW2i9veJouiGo4tL .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #extensionEnd,#mermaid-svg-oW2i9veJouiGo4tL .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #aggregationStart,#mermaid-svg-oW2i9veJouiGo4tL .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL #aggregationEnd,#mermaid-svg-oW2i9veJouiGo4tL .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-oW2i9veJouiGo4tL .edgeTerminals{font-size:11px;}#mermaid-svg-oW2i9veJouiGo4tL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} File InputFile OutputFile IOFile File中有一个filenameIOFile则会继承到2个filename。如果不想要就必须令File成为一个虚基类。但虚基类带来的虚继承需要付出代价除了空间还有虚基类的初始化要由最低端的类负责。如果必须使用虚基类尽可能避免在其中放置数据。 模板 模板元编程Template metaprogrammingTMP是编写基于模板的c程序并执行于编译期间。已经证明TMP是一个图灵完全机器它可以做到任何事情声明变量、执行循环等。TMP擅长的事情有 保证度量单位正确 科学和工程应用中TMP可以确保度量单位的准确使用。优化矩阵运算生成定制的设计模式 对模板而言接口是隐式的多态则是通过模板具现化和函数重载解析发生于编译期。 使用模板可能会导致代码膨胀模板可能生成多个类和函数。因非类型的模板参数造成的代码膨胀即模板参数不是类型而用于其他用途只需用函数参数或类成员变量来替代模板参数即可。而因类型的模板参数造成的代码膨胀如针对参数是int和long生成两种代码但有时它们是相同的降低膨胀的做法是让它们共享代码。 typename还用来标识嵌套从属类型 templatetypename T void func(const T container) {T::const_iterator* it;··· } T::const_iterator上面的T::const_iterator就是嵌套从属名称我们想要的是任意一个STL容器T的迭代器const_iterator但如果T::const_iterator不是类型呢如果T刚好有个静态成员变量叫做const_iterator且it恰好是个全局变量那么上式就不再是声明一个指针而是让T::const_iterator乘以it。 不巧的是C的解析器在模板中遇到一个嵌套从属名称时会假设其不是一个类型。除非我们告诉他方法就是在其前面加上typenametypename T::const_iterator* it。 不过还有例外在继承的基类列表和初始化列表中都不能使用typename。 需要类型转换时为模板定义非成员函数 继续使用有理数类为例 templatetypename T class Rational { public:Rational(const T numerator 0, const T denominator 1);··· }; templatetypename T const RationalT operator*(const RationalT lhs, const RationalT rhs) {}此时如果有 Rationalint oneHalf(1, 2); Rationalint result oneHalf * 2;就会编译错误。 编译器要找到某个operator*接受两个RationalT就需要推导出T。而为了推导出T就需要查看两个参数。第一个RationalT参数oneHalf是Rationalint因此T就一定是int但第二个RationalT参数2是一个int就推导不出T的类型。要知道编译器在模板实参推导过程中不会考虑隐式类型转换函数因此也就不会将int隐式转换为RationalT。 解决方法则是将operator*的合并到其类内 templatetypename T class Rational { public:···friend const RationalT operator*(const RationalT lhs, const RationalT rhs) {} };有趣的点在于虽然使用了friend但与其传统用途访问类的非公有成员毫不相干。为了让类型转换发生到所有实参身上我们需要一个非成员函数。而为了让这个函数被自动具现化我们需要将其声明在类内部。最终在类内声明一个非成员函数的唯一方法就是使用friend。
http://www.hkea.cn/news/14336594/

相关文章:

  • 营销型企业网站核心苏州手机网站
  • 好的深圳网站页面设计优质网站建设服务
  • 昆山市做网站的公司广州顶正餐饮培训学校
  • 西安网站制作服务商网站建设有哪些分类
  • html网站开头怎么做的网站建设行业企业排名
  • 企业网站建设智恒网络asp.net开发微网站开发
  • wordpress建站教程入门手机画设计图软件
  • 平乡县网站建设自建网站平台要多少钱
  • 网站栏目策划书佛山网站建设网站建设收费
  • 网站标题怎么做网站开发交付
  • wordpress简约下载站模板下载推广运营是做什么的
  • 架设个人网站环球设计网站
  • wordPress如何把菜单加入导航seo排名软件免费
  • 企业网站开发外包网站模糊背景
  • 网页无法访问此网站做国外wordpress赚钱
  • 啪啪男女禁做视频网站微网站如何做微信支付宝支付宝支付宝
  • 阿里云建设网站流程什么网站可以做项目
  • 成都企业网站制作哪家好麻城网站建设公司
  • 国内免费建站网站wordpress电商模板
  • 如何建立竞价网站wordpress+移动客户端
  • 厦门市建设路网站旅游网站建设成都
  • 网站怎么做外链用ps做网站页面的大小
  • 网站建设实验分析千川推广官网
  • 张家界建设企业网站工作管理app
  • 潍坊网站建设公司莱芜大集
  • 创意策划网站公众号如何做微网站
  • 陕西省建设厅管理中心网站ui设计流程培训网站
  • 企业网站 数据库设计网站备案幕布照规范
  • 门户网站建设方案内容做网站托管服务器
  • 给人做ppt的网站wordpress网站欣赏