智能指针学习笔记

转载:http://www.cnblogs.com/wuchanming/p/4411878.html

1. 介绍

本文介绍智能指针的使用。智能指针是c++ 中管理资源的一种方式,用智能指针管理资源,不必担心资源泄露,将c++ 程序员 从指针和内存管理中解脱出来,再者,这也是c++发展的趋势(这话不是我说的,见《Effective c++》和《c++实践编程》),应该认真学习一下。

智能指针中,最有名的应该数auto_ptr,该智能指针已经被纳入标准库,只需要包含<memory>头文件即可以使用,另外,TR1文档定义的shared_ptr和weak_ptr也已经实现(我用的gcc版本是gcc 4.6.1),它们的头文件是<tr1/memory> 。除此之外,还有几个好用的智能指针,不过它们属于boost库,不属于STL ,所以,用不用得到,根据自己的需要。不过,了解一下总无妨,正所谓"功不唐捐"嘛。

下面分别介绍auto_ptr,scoped_ptr,scoped_array,shared_ptr,shared_array, weak_ptr 和 intrusive_ptr 。

2. auto_ptr

2.1 为什么要用智能指针

在介绍第一个智能指针之前,先介绍下为什么要使用智能指针。先看下面这个函数:

void f()
{ classA* ptr = new classA; // create an object explicitly ... // perform some operations delete ptr; // clean up(destory the object explicitly) }

这个函数是一系列麻烦的根源,一个显而易见的问题是,我们经常忘了delete 动作,特别是当函数中间存在return 语句时更是如此。然而,真正的麻烦发生于更隐晦之处,那就是当异常发生时,我们所要面对的灾难,异常一旦出现,函数将立刻退离,根本不会调用函数尾端的delete 语句。结果可能是内存遗失。防止这种资源遗失的常见办法就是捕捉所有异常,例如:

void f()
{ classA* ptr = new classA; // create an object explicitly try{ ... // perform some operations } catch(...){ delete ptr; //-clean up throw;//-rethrow the exception } delete ptr; // clean up(destory the object explicitly) }

你看,为了异常发生时处理对象的删除工作,程序代码变得多么复杂和累赘!如果还有第二个对象,如果还需要更多的catch 子句,那么简直是一场恶梦。这不是优良的编程风格,复杂而且容易出错,必须尽力避免。

如果使用智能指针,情况就会大不相同了。这个智能指针应该保证,无论在何种情形下,只要自己被摧毁,就一定要连带释放其所指资源。而由于智能型指针本身就是局部变量,所以无论是正常退出还是异常退出,只要函数退出,它就一定会被销毁。auto_ptr正是这种只能型指针。

2.2 auto_ptr

auto_ptr 是这样一种指针:它是"它所指向的对象"的拥有者,所以,当身为对象拥有者的auto_ptr 被摧毁时,该对象也将遭到摧毁。auto_ptr 要求一个对象只能有一个拥有者,严禁一物二主。更详细的说, auto_ptr 管理的资源必须绝对没有一个以上的auto_ptr 同时指向它。 这是因为资源的拥有者在销毁的时候,会销毁它所拥有的资源,资源不能释放两次,如果同时有两个auto_ptr拥有同一个资源,那么,在第一个auto_ptr销毁以后,第二个auto_ptr就成为野指针了,所以,任何时刻,一个资源只有一个拥有者

下面是上例改写后的版本:

#include <iostream>
#include <memory> using namespace std; void f() { //create and initialize an auto_ptr std::auto_ptr<classA> ptr(new classA); ... //perform some operations }

不需要delete ,也不再需要catch 了。auto_ptr 的接口与一般指针非常相似:operator *用来提取其所指对象,operator-> 用来指向对象中的成员。然而,所有指针算法(包括++在内)都没有定义。

注意,auto_ptr< >允许你使用一般指针惯用的赋值(assign)初始化方式。必须直接使用数值来完成除始化:

std::auto_ptr<classA> ptr1(new classA); //OK std::auto_ptr<classA> ptr2 = new classA;//ERROR

有了上面两条语句,那么下面的问题就很显然了。

std::auto_ptr<classA> ptr; // create an auto_ptr ptr = new classA; // ERROR ptr = std::auto_ptr<classA>(new classA); // ok, delete old object and own new

2.3 auto_ptr 拥有权的转移

上面提到过,绝对没有一个以上的auto_ptr同时指向同一个资源,那么,如果你复制(或赋值)一个auto_ptr指针会发生什么呢?发生拥有权转移,如下所示:

//initizlize an auto_ptr with a new object
std::auto_ptr<classA> ptr1(new classA); //copy the auto_ptr std::auto_ptr<classA> ptr2(ptr1);

在第一个语句中,ptr拥有了那个new 出来的对象。在第二个语句中,拥有权由ptr1 转移到ptr2,此后,ptr2拥有那个对象,ptr1则是一个空指针。

同理,赋值动作也会发生拥有权的转移。

//initizlize an auto_ptr with a new object
std::auto_ptr<classA> ptr1(new classA); //copy the auto_ptr std::auto_ptr<classA> ptr2; ptr2 = ptr1;

在上面的语句中,如果ptr2已经拥有一个对象,则,赋值动作发生时,会调用delete,将该对象删除。

因为auto_ptr 会发生拥有权转移问题,所以,不能完全像使用普通指针一样使用auto_ptr ,下面这个错误的用法演示的auto_ptr 的特性。

//this is a bad example
template <class T> void bad_print(std::auto_ptr<T> p)//p gets ownership of passed argument { //does p own an object? if (p.get() == NULL) { std::cout << "NULL"; } else { std::cout << *p; } //Oops,exiting delete the object to which p refers } int main(int argc, char const* argv[]) { std::auto_ptr<int> p(new int); *p = 42; // change value to which p refers bad_print(p); // Oops,deletes the memory to which p refers *p = 18; // RUNTIME ERROR return 0; }

我们只是想通过print函数,打印对象的值,可是,却不小心把对象给销毁了,这是非常低级的错误,再多用几次auto_ptr 以后,就不会出现这种情况了。如果我们不是通过传值,而是通过传递一个引用会怎么样呢?可以这么做,可是,你得非常小心,千万别在调用的函数里面将资源的拥有权转移了。正确的用法应该声明指针常量,如下所示:

const std::auto_ptr<int>p(new int); *p = 42 // change value to which p refers bad_print(p); // COMPILE-TIME ERROR *p = 18; // OK

注意,auto_ptr 是一个指针,const auto_ptr 要表达的意思是"指针常量,指针不可指向其他资源,但是指针所指之物可以修改",而不是指向常量的指针。所以,const auto_ptr 类似于 T* const p而不是指向常量的指针const T* p 。下面是一个使用auto_ptr 指针的完整示例:

#include <iostream>
#include <memory> using namespace std; //define output operator for auto_ptr //-print object value or NULL template<class T> ostream& operator<< (ostream &strm, const auto_ptr<T> &p) { //does p own an object ? if (p.get() == NULL) { strm << "NULL"; } else { strm << *p; } return strm; } int main(int argc, char* argv[]) { auto_ptr<int> p(new int(42)); auto_ptr<int> q; cout << "after initizlization:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; q = p; cout << "after assigning auto pointers:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; *q += 13; p = q; cout << "after change and reassignment" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; return 0; }

输出结果如下:

after initizlization:
p : 42 q : NULL after assigning auto pointers: p : NULL q : 42 after change and reassignment p : 55 q : NULL

2.4 auto_ptr 注意事项

  1. auto_ptr(以及后面介绍的std::tr1::shared_ptr) 在其析构函数内做delete,而不是delete[]动作,那意味着在动态分配而得到的array上使用auto_ptr(或tr1::shared_ptr)是一个馊主意。但是,这样的代码是可以通过编译的,所以需要用户自己留心。下面的代码就会出现用new []分配资源,用delete而不是delete[] 释放资源一样的问题。

    std::auto_ptr<std::string> aps(new std::string[10]);//资源泄漏 std::tr1::shared_ptr<int> spi(new int[1024]); //资源泄漏
  2. 标准容器需要元素具有可复制和可赋值的特性,而复制和赋值操作会使auto_ptr 发生所有权转移,所以,auto_ptr 不能存放在容器中

3. scoped_ptr

有了上面对auto_ptr 的解释,理解scoped_ptr 就没有什么难度了。scoped_ptr 的名字向读者传递了明确的信息,这个智能指针只能在本作用域中使用,不希望被转让。 scoped_ptr 通过将拷贝构造函数和operator= 函数声明为私有,以此阻止智能指针的复制,也就关闭了所有权转移的大门

scoped_ptr 的用法与auto_ptr 几乎一样,大多数情况下它可以与auto_ptr 相互替换,它也可用从一个auto_ptr 获得指针的管理权(同时,auto_ptr 失去管理权)

scoped_ptr 也具有与auto_ptr 同样的"缺陷"——不能用作容器的元素,但原因不同,auto_ptr 是因为它的转移语义,而scoped_ptr 则是因为不支持拷贝和赋值,不符合容器对元素类型的基本要求

下面的代码演示了scoped_ptr 与auto_ptr 的区别。

auto_ptr<int> ap(new int(10)); // 一个auto_ptr<int> scoped_ptr<int> sp(ap); // 从auto_ptr 获得原始指针 assert(ap.get() == 0); // 原auto_ptr 不再拥有指针 ap.reset(new int(20)); // auto_ptr 拥有新的指针 cout << *ap << "," << *sp << endl; auto_ptr<int> ap2; ap2 = ap; // ap2 从ap 获得原始指针,发生所有权转移 assert(ap.get() == 0); // ap 不再拥有指针 scoped_ptr<int> sp2; // 另一个scoped_ptr sp2 = sp; // 赋值操作,无法通过编译

比起auto_ptr ,scoped_ptr 更明确的表达了代码原始编写者的意图:只能在定义的作用域内使用,不可转让。

4. scoped_array

scoped_array 与scoped_ptr 没什么区别,主要区别就是用 new[] 分配资源,用 delete [] 释放资源,而scoped_ptr 用new 分配资源,用delete 释放资源。用法如下:

#include <iostream>
#include <algorithm> #include <iterator> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; int main(int argc, char* argv[]) { int *arr = new int[100]; //动态分配资源 scoped_array<int> sa(arr);//用scoped_array 对象代理原始动态数组 //scoped_array<int> sa( new int[100]); fill_n(&sa[0], 100, 5); sa[10] = sa[20] + sa[30];//像普通数组一样使用 cout << sa[10] << "\t" << sa[20] << endl; return 0;//在作用域最后,自动释放资源 }

scoped_array 与 scoped_ptr 接口和功能几乎一样,主要区别如下:

  1. 构造函数接受的指针p 必须是new [] 的结果,而不能是new 表达式的结果。
  2. 没有* , -> 操作符重载,因为scoped_array 持有的不是一个普通指针
  3. 析构函数使用delete []释放资源,而不是delete
  4. 提供operator[] 操作符重载,可以像普通数组一样使用下标访问元素
  5. 没有begin() end() 等类似容器的迭代器

上面这个例子,可以很方便的使用vector代替,《Boost 程序库开发指南》的作者并不推荐使用scoped_array。

5. shared_ptr

5.1 shared_ptr 介绍

上面已经介绍了3种智能指针,如果按照重要程度排序,auto_ptr 是最重要的,其次应该算shared_ptr 了,shared_ptr 已经被纳入标准库了,用gcc 的用户只需要#include<tr1/memory>用visual studio 08/10 的用户通过加入头文件#include<memory>即可。

shared_ptr 是一个最像指针的"智能指针",它实现了引用计数的功能,所以,指针可以随意复制,在函数间传递,或者存储在容器里面

shared_ptr 还有两个特有的成员函数,分别是:

  1. unique() 用于检查指针是否唯一的,如果是唯一的,就相当于auto_ptr
  2. use_count() 返回当前指针的引用计数,use_count() 不提供高效率的操作,所以,use_count() 应该仅仅用于测试或者调试。

下面看看shared_ptr 的用法:

#include <iostream>
#include <tr1/memory> #include <assert.h> using namespace std; int main(int argc, char* argv[]) { std::tr1::shared_ptr<int> sp( new int(10));//一个指向整数的shared_ptr assert( sp.unique());//现在shared_ptr 是指针的唯一持有者 std::tr1::shared_ptr<int> sp2 = sp;//第二个shared_ptr ,拷贝构造函数 //两个shared_ptr 相等,指向同一个对象,且引用计数为2 assert(sp == sp2 && sp.use_count() == 2); *sp2 = 100;//使用解引用操作符修改被指对象 assert(*sp == 100);//另一个shared_ptr 同时也被修改 sp.reset(); assert(!sp);//sp 不再持有对象 return 0; }

再看一个复杂一点的例子,用以演示智能指针作为成员变量和函数参数的情况。

#include <iostream>
#include <tr1/memory> #include <assert.h> using namespace std; using namespace std::tr1; class shared{ private: shared_ptr<int> p; public: shared(shared_ptr<int> p_):p(p_){}; void print() { cout << "count:" << p.use_count() << " v = " << *p << endl; } }; void print_fun(shared_ptr<int> p) { cout << "count:" << p.use_count() << " v = " << *p << endl; } int main(int argc, char* argv[]) { shared_ptr<int> p(new int(100)); shared s1(p), s2(p); s1.print(); s2.print(); *p = 20; print_fun(p); s1.print(); }

输出结果如下:

count:3 v = 100 count:3 v = 100 count:4 v = 20 count:3 v = 20

可以看到,我们不用关心shared_ptr 的具体实现,也不需要烦心它的引用计数是多少,我们只需要把它当成一个普通指针使用,再也不用担心资源泄漏。

auto_ptr 不能一物侍二主,所以,拷贝的时候会发生所有权转移,而shared_ptr 则不存在这个问题呢,那么,把一个 auto_ptr 复制给 shared_ptr 或者把一个shared_ptr 复制给auto_ptr 会发生什么呢?答案是编译错误,即你不能这么做。

5.2 make_shared

前面说过,shared_ptr 是最像指针的智能指针,有了shared_ptr ,我们几乎可以抛弃delete了,但是,我们还是用到了new,用到了new 而不delete ,很不对称不是吗,所以,TR1又定义了一个小工具make_shared(类似与make_pair)来帮助我们生成对象,不过,这个功能好像还没有实现,如果,等不及要玩一下,可以用boost库,make_shared 在头文件#include<boost/make_shared.hpp>中定义,使用方法如下:

#include <iostream>
#include <string> #include <vector> #include <boost/make_shared.hpp> using namespace std; int main(int argc, char const* argv[]) { boost::shared_ptr<string> sp = boost::make_shared<string>("make_shared"); cout << *sp << endl; boost::shared_ptr< vector<int> > spv = boost::make_shared< vector<int> >(10, 2); cout << spv->size() << endl; return 0; }

shared_ptr 可以应用于标注库,唯一需要牢记的是,shared_ptr 是一个指针,行为类似于普通指针,知道这一点以后,下面的代码就不难理解了。

#include <boost/make_shared.hpp> #include <iostream> #include <vector> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; int main(int argc, char const* argv[]) { typedef vector< shared_ptr<int> > vs; //声明一个持有shared_ptr 的标准容器类型,元素被初始化为空指针 vs v(10); int i = 0; for (vs::iterator pos = v.begin(); pos != v.end(); ++pos) { (*pos) = make_shared<int>(++i);//使用工厂函数(make_shared)赋值 cout << *(*pos) << ", ";//输出值 } cout << endl; shared_ptr<int> p = v[9]; *p = 100; cout << *v[9] << endl; return 0; }

5.3 shared_ptr 的缺陷(循环引用)

shared_ptr 需要当心循环引用的问题,不然还是会发生资源泄漏。详细信息见这里。

6. shared_array

我们知道scoped_ptr 和 scoped_array 的用法和区别以后,很容易猜到shared_array 的用法了。

shared_array 类似于shared_ptr ,它包装了new[] 操作符在堆上分配的动态数组,同样,使用引用计数机制为动态数组提供了一个代理,可以在程序的生命周期里上期存在,直到没有任何引用后才释放内存。

shared_array 的接口和功能几乎与shared_ptr 是相同的,主要区别如下:

  1. 构造函数接受指针p必须是new[] 的结果,而不是new 分配的资源
  2. 提供operator[] 操作符重载,可以像普通数组一样用下标访问
  3. 没有* -> 操作符重载,因为shared_array 持有的不是一个普通指针
  4. 析构函数使用delete[] 释放资源,而不是delete

shared_array 用法的简单示例:

#include <iostream>
#include <boost/smart_ptr.hpp> #include <assert.h> using namespace std; using namespace boost; int main(int argc, char const* argv[]) { shared_array<int> sa( new int[100]); shared_array<int> sa2 = sa; sa[0] = 10; cout << sa.use_count() << endl; cout << sa[0] << endl; assert( sa2[0] == 10); return 0; }

7. weak_ptr

关于weak_ptr 我是知其然,但不知其所以然。下面的说明和例子都来自《Boost 程序库完全开发指南》,无任何更改,没有理解透彻,怕改错了。

weak_ptr 被设计为与shared_ptr 共同工作,可以从一个shared_ptr 或者另一个weak_ptr 对象构造,获得资源的观测权。但是weak_ptr 没有共享资源,它的构造函数不会引起指针引用计数的增加。同样,在weak_ptr 析构时,也不会导致引用计数减少,它只是一个静静的观察者。

使用weak_ptr 的成员函数use_count() 可以观测资源的引用计数,另一个成员函数expired() 的功能等价于use_count() == 0,但更快,表示被观测的资源已经不复存在。

weak_ptr 没有重载operator* 和 -> ,这是特意的,因为它不共享指针,不能操作资源,这正是它"弱"的原因,但它可以使用一个非常重要的成员函数lock() 从被观测的shared_ptr 获得一个可用的shared_ptr 对象,从而操作资源。但当expired() == ture的时候,lock()函数返回一个存储空指针的shared_ptr 。

下面的代码示范了weak_ptr 的用法:

shared_ptr<int> sp (new int(10)); // 一个shared_ptr assert(sp.use_count() == 1); weak_ptr<int> wp(sp); // 从shared_ptr 创建weak_ptr assert(sp.use_count() == 1); // weak_ptr 不影响引用计数 if (!wp.expired()) // 判断weak_ptr 观察的对象是否有效 { shared_ptr<int> sp2 = wp.lock();//获得一个shared_ptr *sp2 = 100; assert(sp.use_count() == 2); }//退出作用域,sp2 自动析构,引用计数减1 assert(sp.use_count() == 1); sp.reset(); // shared_ptr 失效 assert(wp.expired()); // weak_ptr 将获得一个空指针 assert(!wp.lock());

8. intrusive_ptr

Boost 库实现了该指针,Boost 库不推荐使用intrusive_ptr。

9. 注意事项

  • 在资源管理类中提供对原始资源的访问(Item 15)

    使用智能指针的时候,可以通过get()成员函数,获取原始指针,从而与一下需要用到原始资源的API打交道,如果这样的API特别多,每次都写.get() 不光费时,而且不够清晰,这时,应该提供隐式类型转换。如下所示:

      class Font{ public: .... //隐式类型转换 operator FondHandle() const {return f;} ... };
  • 以独立的语句将newd 对象置入智能指针(Item 17)

应该用独立的语句将newd 的对象置入只能指针,考虑如下调用: processWidget(std::tr1::shared_ptr(new Widget), priority());

在上面的调用中,需要处理以下三件事:

  1. 调用priority
  2. 执行new Widget
  3. 调用tr1::shared_ptr构造函数

c++ 编译器会以什么样的次序完成上面三件事,我们不得而知,如果调用序列如下:

  1. 执行new Widget
  2. 调用priority
  3. 调用tr1::shared_ptr构造函数

万一对priority 的调用导致异常,则new Widget返回的指针就会遗失,我们无法使用,也无法释放该资源,所以,安全的处理方式,应该是这样的:

std::tr1::shared_ptr<Widget> pw(new Widget); processWidget(pw, priority());

10. 总结

在上面所有介绍的智能指针中,auto_ptr ,shared_ptr 和weak_ptr 已经纳入标准库,可以放心使用,而不用担心可移植性的问题。其中auto_ptr 和shared_ptr 最为重要,shared_ptr和普通指针最为相似,不知道该用哪种类型的智能指针的时候,就用shared_ptr 。

参考资料

  • 《C++ 标准程序库》
  • 《Boost 程序库完全开发指南》
  • 《Effective c++》

转载:http://mingxinglai.com/cn/2013/01/smart-ptr/


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++程序编译过程

c程序编译分成四个过程&#xff1a;编译预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接 编译预处理&#xff1a;处理以#为开头 编译&#xff1a;将.cpp文件翻译成.s汇编文件 汇编&#xff1a;将.s汇编文件翻译成机器指令.o文件 链接&#xff1a;汇编生产的目标文件.o…

仿函数(函数对象)

转载&#xff1a;http://www.cnblogs.com/wuchanming/p/4411867.html 本文乃作者学习《C标准程序库》的学习笔记&#xff0c;首先介绍了仿函数&#xff08;函数对象&#xff09;和函数适配器&#xff08;配接器&#xff09;的概念&#xff0c;然后列出STL中所有的仿函数&#x…

C++ template —— 动多态与静多态(六)

转载&#xff1a;http://www.cnblogs.com/yyxt/p/5157517.html 前面的几篇博文介绍了模板的基础知识&#xff0c;并且也深入的讲解了模板的特性。接下来的博文中&#xff0c;将会针对模板与设计进行相关的介绍。 ------------------------------------------------------------…

变量之间的区别

全局变量、局部变量、静态全局变量、静态局部变量的区别 c变量根据定义具有不同的生命周期&#xff0c;会有不同的作用域&#xff0c;主要有六个作用域&#xff1a;全局作用域&#xff0c;局部作用域&#xff0c;文件作用域&#xff0c;类作用域&#xff0c;语句作用域&#xf…

计算机的网络体系以及参考模型

计算机的网络体系以及参考模型一、OSI七层模型二、TCP/IP参考模型三、TCP/IP 五层参考模型四、OSI 模型和 TCP/IP 模型异同比较五、OSI 和 TCP/IP 协议之间的对应关系六、为什么 TCP/IP 去除了表示层和会话层&#xff1f;七、数据如何在各层之间传输&#xff08;数据的封装过程…

C++ 模板详解(二)

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html 四、类模板的默认模板类型形参 1、可以为类模板的类型形参提供默认值&#xff0c;但不能为函数模板的类型形参提供默认值。函数模板和类模板都可以为模板的非类型形参提供默认值。 2、类模板的类…

c++类对象的创建方式

对象创建限制在堆或栈 c类对象的创建方式对象创建限制在堆或栈C 中的类的对象的建立模式如何将类限制在堆上呢&#xff1f;C 中的类的对象的建立模式 C 中的类的对象的建立模式分为两张&#xff1a;静态建立&#xff0c;动态建立 静态建立&#xff1a;由编译器为对象在栈空间…

C++ 模板详解(一)

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C模板 模板是C支持参数化多态的工具&#xff0c;使用模板可以使用户为类或者函数声明一种一般模式&#xff0c;使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。 模板是一种对类…

剑指Offer09. 用两个栈实现队列

class CQueue { public:stack<int> stack1,stack2;CQueue() {//初始化栈while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void appendTail(int value) {stack1.push(value);}int deleteHead() {if(stack2.empty()){while(!stack1.empty()){…

rk3588 之启动

目录 uboot版本配置修改编译 linux版本配置修改编译 启动sd卡启动制作spi 烧录 参考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用这两个配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…

C++引用详解

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用&#xff1a;就是某一变量&#xff08;目标&#xff09;的一个别名&#xff0c;对引用的操作与对变量直接操作完全一样。 引用的声明方法&#xff1a;类型标识符 &引用名目标…

剑指Offer03.数组中重复的数字

找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 示例 1&#xff1a; 输入&…

C++ 模板全特化中的函数特化

转载&#xff1a;http://blog.csdn.net/rain_qingtian/article/details/15815251 [cpp] view plaincopy print?#include <iostream> using namespace std; template<typename T> bool isLess(T x, T y) { cout << "general version\n&q…

c++面向对象总结

c面向对象总结什么是面向对象&#xff1f;面向对象的三大特性重写和重载的区别隐藏和重写&#xff0c;重载的区别什么是多态&#xff1f;多态如何实现什么是面向对象&#xff1f;面向对象的三大特性 面向对象&#xff1a;对象是指具体的某一个事物&#xff0c;这些事物的抽象就…

类模板static成员的使用

转载&#xff1a;http://blog.csdn.net/ljq32/article/details/7911390 1. 与普通类的static成员一样&#xff0c;类内部声明一次&#xff0c;类外部定义一次&#xff0c;定义时可以设置也可以不设置初始值; 2. 类模板内部声明与普通类的static成员一致&#xff1a; [html] vi…

Linux网络编程服务器模型选择之循环服务器

转载&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617608.html 在网络程序里面&#xff0c;通常都是一个服务器处理多个客户机&#xff0c;为了出个多个客户机的请求&#xff0c;服务器端的程序有不同的处理方式。本节开始介绍Linux下套接字编程的服务器模型选择&#xff…

剑指Offer04. 二维数组中的查找

在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该整数。 相当于二叉搜索树,左孩子比根节点小&…

Linux网络编程服务器模型选择之并发服务器(上)

转载&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617666.html 与循环服务器的串行处理不同&#xff0c;并发服务器对服务请求并发处理。循环服务器只能够一个一个的处理客户端的请求&#xff0c;显然效率很低。并发服务器通过建立多个子进程来实现对请求的并发处理。并发…

剑指Offer10- II. 青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如计算初始结果为&#xff1a;1000000008&#xff0c;请返回 1。 示例 1&#xff1a; 输入&a…

Linux网络编程服务器模型选择之并发服务器(下)

转载&#xff1a;http://www.cnblogs.com/lizhenghn/p/3618986.html 前面两篇文章&#xff08;参见&#xff09;分别介绍了循环服务器和简单的并发服务器网络模型&#xff0c;我们已经知道循环服务器模型效率较低&#xff0c;同一时刻只能为一个客户端提供服务&#xff0c;而且…