C++primer学习笔记
文章平均质量分 69
thefutureisour
ZTE基带部码农,平时工作很忙,不再更新opencv相关内容,也不解答大家问题了。
展开
-
类成员指针
先看代码:class Student{public: Student(int i, string nm):schoolNumber(i),name(nm){} void getInfo()const { cout<<"name: "<<name<<endl <<"SchoolNumber: "<<schoolNumber<<endl; } void setInfo(st原创 2012-10-18 08:18:56 · 1262 阅读 · 0 评论 -
模板的实例化
编译器对待模版时,总会用它产生特定类型的版本,这个过程成为实例化。其中函数模板在调用时或者用它对指向函数的指针初始化或者赋值时实例化,类模板在引用实际模板类型时实例化。尤其对于函数模板,编译器通常会进行实参的推断。伴随而来的问题跟函数重载类似,就是确定匹配的模板。对于模板,多个类型的实参必须完全匹配,不能依靠隐式的类型转换:对于模板:template int compare(cons原创 2012-07-17 09:15:53 · 4448 阅读 · 0 评论 -
容器适配器
首先,我们要明白适配器是干什么的?其实就是一个接口转换装置,是得我们能用特定的方法去操作一些我们本来无法操作的东西。举一个例子,比如你的一个设备支持串口线,而你的电脑支持的是usb口,这时候,我们没有必要重新买一个支持usb的设备,只需要一根串口转usb口的小玩意,让你的设备能够连接到usb插口上,而它就是适配器。那么C++中的容器适配器是干什么的呢?可以做一个类比,我们已有的容器(比如vec原创 2012-07-16 16:22:40 · 12686 阅读 · 2 评论 -
句柄类与继承
前一小节《容器与继承》http://blog.csdn.net/thefutureisour/article/details/7744790提到过:对于容器,如果定义为基类类型,那么则不能通过容器访问派生类新增的成员;如果定义为派生类类型,一般不能用它承载基类的对象,即使利用类型转化强行承载,则基类对象可以访问没有意义的派生类成员,这样做是很危险的。对这个问题的解决办法,是使用容器保存基类的指原创 2012-07-14 15:16:17 · 4334 阅读 · 6 评论 -
容器与继承
如果使用容器来成放类成员的话,会遇到一些问题。如果容器类型定义为基类类型,那么虽然可以把派生类装进容器中,但是不能通过容器访问派生类自己的public成员;如果把容器定义为派生类类型,那么不能把基类类型装进容器中。虽然可以显式的使用强制类型转化把基类转化成派生类,放入容器,但是如果使用这些元素的派生类成员时,它们将会是未初始化的。下面通过一个例子来说明:class Base{p原创 2012-07-13 17:15:24 · 2634 阅读 · 0 评论 -
基类与派生类的作用域
先把定义的类列出来class Base{public: Base(int i = 0):val(i){} int getVal(){return val;}; int getVal1(){return val;} void print(){cout<<"base"<<endl;} void getInt(){cout<<"do nothing"<<endl;} virtual原创 2012-07-13 16:40:45 · 3060 阅读 · 1 评论 -
C++中const限定符小结
c++中新增加了一种限定符:const限定符。它的作用是是得它限定的对象(或者内置类型的变量)不能改变。举一个简单的例子,当我们定义const int bufSize = 512时,变量bufSize虽然还是左值,但是它已经不能被修改了。也正是因为不能被修改的原因,我们需要在定义时对其初始化。有一点需要注意,当我们在一个文件中定义一个普通全局变量(非const)时,在整个程序中都可以访问,原创 2012-06-07 17:50:43 · 1078 阅读 · 0 评论 -
内联函数
在C++中我们经常把一些小的操作也定义成函数,比如:const string &shorterString(const string &s1,const string &s2){ return s1.size()<s2.size()?s1:s2;}这样做的好处是:1.阅读和理解一个函数调用比阅读和理解一条表达语句要容易的多2.如果需要修改,那么只用修改函数,而不用在使用语句的原创 2012-06-13 09:02:29 · 925 阅读 · 0 评论 -
重载函数
函数的重载对于出现在相同作用域的两个函数,如果它们的名字相同,而行参不同,则称为重载函数。举一个简单的例子,假设电话号码本中有序号、人名,号码等信息。我们希望通过一个输入其中的一个来或得这个人完整的信息。要完成这个功能,我们需要定义3个函数: Record lookup(const Account&); // find by Account Record lookup(con原创 2012-06-13 11:13:46 · 815 阅读 · 0 评论 -
this指针的使用
当我们调用一个类对象的时候,就会有一个this指针与调用成员函数的对象绑定在一起。虽然成员函数可以显式的使用这个指针,但是一般在什么时候使用呢?当我们需要使用这个类的整体而不是其中的某个成员时,就需要this指针了。最常见的情况,就是一个函数返回调用该函数对象的引用:char Screen::get(index n,index c)const{ index row = n * wid原创 2012-06-30 19:11:57 · 2295 阅读 · 0 评论 -
包含编译模型
对于一般的程序员,总是习惯把类、函数的声明放在头文件件中,而把它们的定义放在对应的源文件中(这个源文件要#include对应的头文件),在主程序中,包含头文件即可。这是因为,只要在编译时可以看到函数的声明,编译就能通过;类似的,定义一个类的对象时,类定义必须可用,但是类成员的就不一定必须定义好了,只要有声明就行。但是对于函数模板或者类模板,这样做就行不通了。因为在实例化模板时,编译器必须能够访原创 2012-07-18 08:30:28 · 1658 阅读 · 1 评论 -
泛型句柄
在《句柄类与继承》http://blog.csdn.net/thefutureisour/article/details/7746582中,我们介绍过句柄。句柄其实就是一个复杂一点的智能指针,不仅在创建、复制、赋值,删除对象时,能够通过引用计数管理指针,而且通过动态绑定,实现了多态:既可以管理基类,可以管理派生类。但是它有一个小的缺陷,因为当时我们并没有介绍模板,所以这个句柄能管理的类型是固定的。原创 2012-07-19 10:38:00 · 1105 阅读 · 0 评论 -
运行时类型识别
运行时的类型识别(RTTI)分为两类:1.typeid操作符2.dynamic_cast操作符先看第二种:dynamic_cast完成将基类类型的指针或者引用安全转化为派生类类型的指针和引用。对于指针,如果转化失败,则将指针置为NULL,对于引用类型,因为没有NULL,所以直接抛出bad_cast异常。下面是对应的例子:#include using namespace st原创 2012-10-11 13:54:11 · 2137 阅读 · 0 评论 -
多重继承
对于大多数程序,似乎是用单个基类的继承就够用了,但是对于一些复杂的模型,可能是用单个基类无法建模:比如孩子可能继承了父亲和母亲两个人的特征。这时就要用到多重继承了。先看一个简单的例子:class Base1{public: Base1(int i = 1,double d = 1.1, const string s = "word1"):ival1(i),dval1(d),s1(s){原创 2012-07-25 16:03:24 · 1207 阅读 · 0 评论 -
基类与派生类
初学C++的时候,很多人都很头疼各种访问标号下基类与派生类的关系,其实,死记硬背肯定不是一个好的办法,要知道它们之间的关系,先要分析一下访问标号是如何产生的:在没有继承之前,类的只有两类用户:类本身和类的使用者。把类成员通过public和private划分恰好体现了这一分割:类的使用者只能访问类的public部分,它们一般是类的接口;而类成员则既可以访问public又可以访问private部分原创 2012-07-10 11:36:48 · 10460 阅读 · 0 评论 -
派生类的构造函数和复制控制
因为派生类是从基类继承而来的,所以包含了基类的一些成员,所以在写派生类的构造函数和复制控制函数时,必须考虑基类的影响。先说构造函数,派生类的构造函数中,并不直接初始化基类的成员,而是调用基类的构造函数初始化基类的部分:class Item_base{public: //构造函数 Item_base(const std::string &book = "",double sales_原创 2012-07-13 14:40:43 · 5346 阅读 · 0 评论 -
命名空间
为什么我们要有命名空间这个概念,主要是因为,在大型C++项目的开发过程中,很有可能出现各种作用域中的名字重复的情况。有可能是一个程序中不能模块中的名字重复,甚至可能是因为使用别的公司的库而产生的命名重复。在没有命名空间这个概念之前,程序员只能把名字起的比较长。但这样也只是减少了重复的可能性。而且大量非常长的名字给程序员增添了负担。命名空间的出现解决了这个问题。简单的说,命名空间就是一个起了名字原创 2012-07-24 15:12:55 · 1480 阅读 · 0 评论 -
异常处理
在C++中,定义了标准异常类,我们可以使用它们来进行异常处理。它们的关系如下:基类是exception,他有4个派生类:bad_alloc,bad_cast,runtime_error,logic_error;其中runtime_error有3个派生类:overflow_error、underflow_error、range_error;logic_error有个派生类:domain_error、原创 2012-07-20 14:43:06 · 1575 阅读 · 0 评论 -
类模板
这一小节专门介绍类模板,先看一个简单的例子:template class Test{public: Test(Type val):value(val){} void set(const Type &val){value = val;} Type get(){return value;}private: Type value;};int main(){ Test原创 2012-07-18 22:29:46 · 3384 阅读 · 0 评论 -
模板特化
C++的这个功能让人开始的时候有点摸不着头脑:既然我们定义了模板,为什么还要特化它呢?简单的理解,对于一些特殊的类型,使用针对这个类型写的特化的模板,与泛型的模板相比,可能效率更高。举个例子,我们之前写过一个函数模板比较大小://模板函数template int compare(const T &v1,const T &v2){ if(v1<v2) return -1; if原创 2012-07-19 14:51:51 · 1254 阅读 · 0 评论 -
函数模板
模板从大体上,可以分为两种:函数模板和类模板。函数模板是算法库的基础,类模板是建立标准库容器和迭代器的基础。这一小节我们只介绍函数模板。个人觉得,模板是C++对C的一个非常有力的扩充,即使我们不使用面向对象的机制,仅仅写面向过程的程序,它也是很有用的。因为它可以很大程度上避免了因为参数和函数返回值的类型不确定而引起的函数重载问题。举一个例子,当我们要编写比大小的函数时,总会碰到这样的问题:原创 2012-07-17 09:01:15 · 5291 阅读 · 0 评论 -
单参数的构造函数带来的隐式转换
构造函数会引起一个不引人注意的问题:用单个实参来调用的构造函数定义了从从形参类型到类类型的一个隐式转换。举个例子说:class Sales_item{public: std::istream& input(std::istream& in); std::ostream& output(std::ostream& out); inline double avg_price()原创 2012-06-30 22:49:51 · 6017 阅读 · 0 评论 -
类的初始化
类的初始化通产有3种类型:使用初始化列表,在构造函数体中赋值,以及使用默认构造函数。先说前两种:初始化列表与在构造函数体中赋值的区别在哪里呢?主要有两点:第一,有的成员不能使用函数体中的“=”初始化。这其实就是初始化与赋值的区别:比如比如const 成员,引用类型,以及没有定义默认构造函数的类,它们都必须在定义时初始化,而不能赋值,这时候我们的初始化列表就派上用场了。第二,初始化的原创 2012-06-30 22:11:46 · 3181 阅读 · 0 评论 -
类的声明
类的定义大家肯定都会,但是类的声明是是用来干什么的呢?举一个最简单例子,比如我们要使用一个链表,而链表的每个元素是一个类,那么我们就需要类的声明了: class linkStudent { Student std; linkStudent *next; linkStudent *prev; }; 但是有一点要特别注意,当你是声明这个类以后,由于你并没有定义它,所原创 2012-06-30 18:27:13 · 931 阅读 · 0 评论 -
3种容器的特点分析
选用哪种容器,需要考虑的问题无非是两点:容器的特点和我们需要对数据进行如何操作。下面先简要介绍一下vector、list和deque三种容器的特点:vector容器相当于数组,但是不需要定义它的大小。vector有一个重要的特征,就是:它里面的数据时连续储存的!理解了这一点,就不难理解vector的两个重要的特点:支持快速的随机访问和随机插入或者删除元素时效率低下。因为是连续储存的,所以当原创 2012-06-19 17:25:23 · 1366 阅读 · 0 评论 -
友元
友元一般情况下,一个函数如果不是一个类的成员函数,是不能访问该类的私有成员的;同理其他类也不能访问别的类的私有成员。但是,友元提供了一种机制,是得我们可以在别的类中或者其他函数中访问一个类的私有部分。只需要在待访问的这个类中加入以friend开头的这个函数或者这个类的声明:class Sales_item{public: std::istream& input(std::istrea原创 2012-07-01 09:45:18 · 2219 阅读 · 0 评论 -
管理指针成员
为什么当类的成员有指针时,需要特别注意?因为当一个指针复制给另一个指针时,两个指针指向同一个对象,使用二者之一修改指向的对象,更特殊的,如果使用一个指针删除了对象时,另一个指针还以为对象存在,这就会造成很大的灾难。通常有3种方法管理指针成员:1.常规管理。就是将一个指针复制给另一个指针,这样做的好处是不需要额外定义复制控制,但是会出现前面所提到的问题。2.智能指针。虽然这些指针共享原创 2012-07-03 15:47:05 · 913 阅读 · 0 评论 -
使用fstream读写文件
下面通过一个例子来说明如何使用: #include #include #include #include using namespace std;void process(string s){ cout<<s;}int main(){ //vecot中存放的是一系列需要打开的文件的名字 vector files; files.push_back("a.t原创 2012-06-20 16:35:45 · 7254 阅读 · 2 评论 -
iostream使用时注意事项
标准IO库在3个头文件中定义:iostream是从流中读写,fstream是从文件的读写,而sstream是从字符串中去读写。IO对象有一个很重要的特点,就是不能复制或者赋值。这意味两点:第一,IO对象不能储存在容器中,第二,形参或者函数的返回值也不能是IO类型。如果需要传递IO对象,则需要使用引用。IO操作有可能是正确的,也有可能是错误的。错误的IO操作将会导致严重的后果,因此IO可定义原创 2012-06-20 13:55:44 · 1198 阅读 · 0 评论 -
容器的注意事项
容器虽然好用,但是使用时很容易出现访问越界的情况。越界经常出现在两种情况:1.添加或者删除元素会导致容器的大小变化,确保迭代的首末地址重新加载。#include #include using namespace std;int main(){ vector ival; for(int i = 0; i < 10; ++i) ival.push_back(i);原创 2012-06-19 11:16:13 · 860 阅读 · 0 评论 -
指向函数的指针
函数指针是指向函数(而不是一般的数组,变量)的指针,这个特定的函数由其返回值和形参表决定,与函数名无关: bool (*pf)(const string &, const string &); 这个语句将 pf 声明为指向函数的指针,它所指向的函数带有两个 const string& 类型的形参和 bool 类型的返回值。函数指针的定义非常冗长,可以使用typedef来简化定义:t转载 2012-06-13 11:48:52 · 790 阅读 · 0 评论 -
函数中实参到形参的传递
参数的传递就是用实参初始化对应的形参。非引用形参:基本的情况:复制实参的值。函数并没有访问调用函数所传递的实参本身,所以不会改变实参的值。void swap(int x, int y){ int tmp; tmp = x; x = y; y = tmp;}int main(){ int a = 1,b = 2; cout<<"a = "<<a<<"\t"<<"原创 2012-06-11 16:41:05 · 6825 阅读 · 0 评论 -
前置操作与后置操作
自增和自减操作符为什么推荐我们使用前置运算++i,而不是后置运算i++?因为使用前置运算的工作更少时,只需要加1后返回结果就行了,而后置运算需要建立变量保存加1后的值,并把加1前的值返回。但是,有一种情况下我们一般都是用后置运算,就是访问容器或者数组时:#include #include using namespace std;int main(){ vector n原创 2012-06-06 14:23:32 · 1862 阅读 · 1 评论 -
泛型算法库简介
什么是泛型算法?通俗的说,就是这个写算法是不依赖数据类型的。比如查找某个元素,可以是对vector查找,也可以对list查找,甚至是对数组查找;这些算法的输入通常是一对迭代器来表明算法作用的范围。下面的例子显示了使用find函数和count函数。 //输入12个数 vector ival; for(int i = 0;i < 6;++i) { ival.push_back(i);原创 2012-06-26 22:18:17 · 2327 阅读 · 0 评论 -
重载操作符
重载操作符就是让操作符作用域非内置类型时也有自己独特的意义。对于内置类型,当操作符作用于它们时,编译器会规定操作的意义:两个int型数据相加的结果与数学运算的加法相同。但是对于非内置类型,比如类或者枚举类型,编译器并没有规定操作符作用于它们的意义。有些时候,这样做是合理的,比如对于两个Student类对象(其中的数据成员有姓名、学号),对它们进行加法操作的确没有什么意义;但是有的时候,我们却希原创 2012-07-06 14:48:23 · 13470 阅读 · 3 评论 -
派生类与基类间的转化
派生类到基类:调用函数时:将派生类对象传递给希望接受基类引用的函数,此时并不发生派生类到基类的类型转化。因为引用直接绑定到派生类上,对象并没有复制,只是将派生类的基类部分的地址传递给基类型的引用。将派生类对象传递给接受基类对象(并非它的引用)的函数时,形参类型是固定的,只是用派生类对象中的基类部分初始化或赋值基类对象。初始化赋值时:对基类进行初始化或者赋值,实际上是在调用构造函原创 2012-07-10 18:31:38 · 2127 阅读 · 0 评论 -
虚函数与动态绑定
在定义基类时,我们希望基类中的有些函数可以在派生类中重新定义。比如,我们定义了基类记录的书,可以求出买了多少书花了多少钱;而在派生类中,我们定义的是打折的书,还是要计算买了多少书花了多少钱。这时,就需要重新定义计算钱数的函数了。注意,这里的重新定义,与之前讲过的函数重载或者操作符重载不同:后面两类,是通过不同的形参,返回值类型来让编译器判断到底使用的是哪个函数,在程序执行前就能判断;而这里的重新定原创 2012-07-10 16:21:17 · 5826 阅读 · 3 评论 -
迭代器小结
在iterator头文件中,定义了3中迭代器:插入迭代器,iostream迭代器,以及反向迭代器。先看插入迭代器。顾名思义,插入迭代器是往容器里添加元素的,分为三种:back_inserter、front_insert,以及insert,它们分别实现从头部、尾部以及任意地方插入。通过一个简单的例子就能看出它们的区别 #include #include //算法库#incl原创 2012-06-27 18:05:44 · 834 阅读 · 0 评论 -
复制控制
我们知道一个类的构造函数指明了当我们定义一个类的对象时会发生什么,这一小节主要讨论另外几个与类的创建及删除有关的概念:复制构造函数(当复制一个类的对象时会发生什么),赋值构造操作符(当对类的对象进行赋值操作时会发生什么),以及析构函数(撤销这个类的对象时会发生什么)。这三个函数统称为复制控制。从它们的作用来看,其实它们非常重要,但是为什么我们平时不太注意他们,甚至有些书籍也对他们不太提及呢?因为如原创 2012-07-02 16:22:36 · 1067 阅读 · 0 评论 -
C++变量的初始化规则
如果在定义时我们没有对变量初始化,那么系统会帮你初始化变量。具体的初始化规则取决于变量的类型和它所处的位置。对于内置类型,全局的,命名空间的,局部静态的变量都初始化为0,而函数体内变量不进行自动初始化。对于类类型,通过默认构造函数进行初始化。如果没有定义默认构造函数,那么我们必须显式的初始化它,我们不能定义一个既没有构造函数,也没有显式初始化的类类型的变量。原创 2012-06-01 13:30:47 · 1454 阅读 · 0 评论