??xml version="1.0" encoding="utf-8" standalone="yes"?>
Z么?因ؓq是一ơ方法论的学习过E,而不仅仅是一U语a的学习?/span>
ȝ本书Q按郭dU的话说Q得有过日子的心Q就一一读下去Q体会,反省Q动手练习?/span>
具体到我Q我看的是英文第四版Q用的编译器?/span>MinGW。这本书不仅可以学习C++Q也是一本很好的学习p的教材。英语很地道Q而且对于词汇量的要求也不是很高(相对Martin大叔的书来说Q?/span>
之所以选择MinGW只是因ؓ?#8220;?#8221;Q因为我qx主要是用Java?/span>CQ其实工作中是没啥机会?/span>C++的。ؓ了把主要_֊攑֜C++本nQ所以ƈ没有?/span>VC之类庞大?/span>IDE工具?/span>
我看书的q度的确不算快,只有周一到周五这5天,周六、周日都用在?/span>huohuo鬼上,基本上没啥精力看书了?/span>
在看“C++ primer”的这4个月的时间里我还在业余时间里做了两个项目。所以看书的旉q不是很规律。但是我的经验就是坚持?/span>
我很享受q?/span>4个月的时光。有Ӟ我觉得这书咋像小说一样好看呢Q上下班的\上,我都想下一大师会讲些啥呢Q有Ӟ写着写着java,甚至有些恍惚Q偶想Q这要是C++多好Q可以这栯样写了?/span>
感谢LippmanQ带l我的快乐?/span>Bye-Bye?/span>
首先Q这真是一个有的命题。我老喜Ƣ。这让我惌v的是Java?/span>clone。(写下q句话时我还没有看这一节,只是一个预感)
q瘾啊。(写下q句话时我刚刚读完这一节,q没有来得及整理W记Q理思\Q?/span>
什么叫做拷贝构造函敎ͼ
拯构造函数有一个Ş参,q且q个形参是指向同一个类的对象的引用。(The constructor that takes a single parameter that is a (usually const) reference to an object of the class type itself is called the copy constructor.Q?/span>
c++支持两种形式的初始化Q?/span>
1. 直接初始化,使用初始化式Q?/span>initializerQ,直接调用对应的构造函数?/span>
2. 拯初始化,使用’=’。先调用构造函数生成时对象,然后再调用拷贝构造函数把临时对象拯到我们要创徏的对象里。(Copy-initialization first uses the indicated constructor to create a temporary object. It then uses the copy constructor to copy that temporary into the one we are creatingQ?/span>
string null_book = "9-999-99999-9"; // copy-initialization string empty_direct; // direct-initialization |
对于cȝ对象Q当指定单一的实参或者ؓ了拷贝明构Z时对象时Q才能用拷贝初始化。(For objects of class type, copy-initialization can be used only when specifying a single argument or when we explicitly build a temporary object to copy.Q?/span>
ifstream file2 = "filename"; // error: copy constructor is private // This initialization is okay only if // the Sales_item(const string&) constructor is not explicit Sales_item item = string("9-999-99999-9"); |
额外唠唠Sales_item item = string("9-999-99999-9");
只有?/span>Sales_item的构造函C没有?/span>Sales_item(const string&)q个构造函数声明ؓexplicitӞq个初始化才正确。这其中包含了一个隐式的cd转换Q由实参string("9-999-99999-9");生成Sales_itemcd的对象,然后再调?/span>copy函数拯l?/span>item?/span>
但是Q咋q么多但是呢Q如果这么写Sales_item item2 = item;又不一样了。这样就变成了调用赋值操作符?/span>
复制构造函数是逐成员拷贝(memberwise copyQ?/span>
即我们不能?/span>arrayq行copyQ但是如果一个类包含有数l类型的成员Q合成的复制构造函C会拷贝这个的数组。数l中的每个元素都会进行复制。(Even though we ordinarily cannot copy an array, if a class has a member that is an array, then the synthesized copy constructor will copy the array. does so by copying each element.Q?/span>
class Foo { public: Foo(); // default constructor Foo(const Foo&); // copy constructor // ... }; |
Ҏ(gu)Q?/span>是把拷贝函数声明ؓprivate?/span>
如果一个类里面定义了一个构造函敎ͼ~译期就不会再ؓq个cȝ成默认构造函敎ͼ而需要自己来定义了?/span>
温习一下什么是默认构造函敎ͼQ唉Q忘记的真快啊)
默认构造函数没有Ş参。如果定义的对象不提供初始化式(initializerQ就会调用默认构造函数。(The default constructor is used whenever we define an object but do not supply an initializer.Q?/span>
如果一个构造函Cؓ所有Ş参提供缺省的实参Q也可以叫做是默认构造函数。(A constructor that supplies default arguments for all its parameters also defines the default constructor.Q?/span>
如果c自己没有定义赋值操作符Q那么编译器合成赋值操作符。(注意Q这zL~译器干的)
操作W重载的形参列表Q这里要包括this形参Q如果操作符是成员函数的话)以及q回值必d操作W所需要的操作数相同?/span>
q句话咋理解呢?
Sales_item trans, accum; trans = accum; |
1. 形参列表
操作W?/span>’=’是二元操作符Q它的操作数是两个:W一个操作数是左边的操作敎ͼW二个操作数是右边的操作数。上面这个例子,如果我们需要重?/span>’=’Q第一个操作数是被l定?/span>this指针上,W二个操作数是一?/span>Sales_itemcd的对象一般来_双操作C般都是作?/strong>const引用传递?/span>Q?/span>Usually, the right-hand operand is passed as a const reference.Q?/span>
Sales_item& operator=(const Sales_item &);
2. q回?/span>
赋值操作符的返回值类型应该和内置cd的返回值类型相同。(The return type from the assignment operator should be the same as the return from assignment for the built-in typesQ内|类型的赋D回的是左操作数的引用Q因此赋值操作符也必返回和它的cȝ型相同的引用。具体到Sales_itemQ?/span>’=’操作W重载的q回值是Sales_item的引用?/span>
class Sales_item { public: // other members as before // equivalent to the synthesized assignment operator Sales_item& operator=(const Sales_item &); }; |
合成赋值操作符也是逐个成员赋|memberwise assignmentQ。对于数l是Ҏ(gu)个元素进行赋倹{?/span>
// equivalent to the synthesized assignment operator Sales_item& Sales_item::operator=(const Sales_item &rhs) { isbn = rhs.isbn; // calls string::operator= units_sold = rhs.units_sold; // uses built-in int assignment revenue = rhs.revenue; // uses built-in double assignment return *this; } |
// p points to default constructed object Sales_item *p = new Sales_item; { // new scope Sales_item item(*p); // copy constructor copies *p into item delete p; // destructor called on object pointed to by p } |
以上q个例子说明在两U情况下会调用析构函敎ͼ
q一步说明的是指向对象的指针或者是引用即Z作用域,析构函数也是不会被执行的。就q是以上面的例子来说明:
// p points to default constructed object Sales_item *p = new Sales_item; { // new scope Sales_item *ghost = new Sales_item; Sales_item item(*p); // copy constructor copies *p into item delete p; // destructor called on object pointed to by p } |
ghostq个鬼即Z作用域实际上也是没有被删除滴Q占用的内存I间也是没有被释放滴。这LE序跑上N遍,机器׃惌牛一h了。哎Q(
另外当容器撤销Ӟ容器中的元素的析构函C会被调用Q?/span>Destructors are also run on the elements of class type in a container - whether a library container or built-in array - when the container is destroyedQ?/span>
析构函数用来释放构造函数或对象生存周期内所需的资?/span>( Ordinarily they are used to relinquish resources acquired in the constructor or during the lifetime of the object.)
另外析构函数也不仅仅是用来释放资源。析构函数可以执行类设计者希望执行的M操作Q这些操作和cd象用完毕之后执行。(A destructor is not limited only to relinquishing resources. A destructor, in general, can perform any operation that the class designer wishes to have executed subsequent to the last use of an object of that class.Q?/span>
首要原则是:如果c需要析构函敎ͼ那么它一定也需要拷贝构造函敎ͼcopy constructorQ和赋值操作符Q?/span>assignment operatorQ?/span>
析构函数和拷贝构造函敎ͼcopy constructorQ和赋值操作符Q?/span>assignment operatorQ不同,是即你定义了自己的析构函敎ͼ?/span>deleteӞ先调用你自己的析构函敎ͼ然后q是要调用合成的析构函数Q?/span>synthesized destructorQ?/span>
析构函数执行的顺序:和构造函数恰恰相反。按照成员在c里面定义的相反的顺序撤销成员。(it destroys the members in reverse order from which they are declared in the class. For each member that is of class type, the synthesized destructor invokes that member's destructor to destroy the object.Q?/span>
自定义析构函数和合成析构函数的执行顺序:先自定义析构函数Q然后再执行合成析构函数?/span>
class Sales_item { public: // empty; no work to do other than destroying the members, // which happens automatically ~Sales_item() { } // other members as before }; |
需求描qͼ
大师抽象出来两个操作Q?/span>
void remove_Msg_from_Folders();
void put_Msg_in_Folders(const std::set<Folder*>&);
如果我们定义自己的拷贝构造函敎ͼ那么我们必须昑ּ拯M我们惌拯的成员,因ؓ昑ּ定义考别构造函数是不会自动拯M东西的。(When we write our own copy constructor, we must explicitly copy any members that we want copied. An explicitly defined copy constructor copies nothing automatically.Q就是说你要打算自己q就全部自己qԌ别指望编译器会ؓ你拾遗补~?/span>
大师l出的办法就是对指针q行装Q定义一个新c,它包含有指针Q以及用的ơ数。下面这个类的定义高Q实在是高!是q个友元的定义?/span>
// private class for use by HasPtr only class U_Ptr { friend class HasPtr; int *ip; size_t use; U_Ptr(int *p): ip(p), use(1) { } ~U_Ptr() { delete ip; } }; |
包含指针成员的对象经帔R要定义拷?/span>-控制成员。(Objects with pointer members often need to define the copy-control members.Q?/span>
拯-控制成员包括:
l 赋值操作(assignment operatorQ?/span>
l 析构函数
l 拯构造函敎ͼcopy constructorQ?/span>
smart pointerc,使用计数器来理?/span>smart pointercR拷?/span>-控制成员都会影响计数器的倹{?/span>
l ‘.’Ҏ(gu)作符Q?/span>’*’解引用操作符的优先
‘.’Ҏ(gu)作符的优先高于解引用,所以要q样子写Q?/span>
Sales_item *sp = &item1; (*sp).same_isbn(item2); // run same_isbn on object to which sp points |
l valuelike classes?/span>pointlike classes有什么区别?
q仅仅是一U很说法而已Q?/span>valuelike classesQ它的?/span>copy后实际上是对应两个对象;?/span>pointlike classesQ?/span>copy的是指针Q因?/span>copy后实际上是指向的同一个对象?/span>
大师仅仅写了q么一个类定义的简单的例子Q却有这么多的东西可以说说啊
class Sales_item { public: // operations on Sales_item objects double avg_price() const; bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } // default constructor needed to initialize members of built-in type Sales_item(): units_sold(0), revenue(0.0) { } private: std::string isbn; unsigned units_sold; double revenue; }; double Sales_item::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } |
· Member: data, functions or type definitions.
· 构造函?/span>Constructors初始化:
// default constructor needed to initialize members of built-in type Sales_item(): units_sold(0), revenue(0.0) { } |
q种写法是初始化内置数据cd?/span>
· const:表示functions不会修改数据成员。这可以用在查询或者判断类型的functions上?/span>
· 成员函数member functions必须在类的内部声明,成员函数的定义是否放在类定义里面是可选的。如果放在类里面Q那?/span>
c背后蕴늚基本思想是数据抽象和装。(The fundamental ideas behind classes are data abstraction and encapsulation.Q数据抽象这U技术依赖于接口和实现的分离。(Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation.Q?/span>
多么_的话Q包含了面向对象的深d义。大师啊?/span>
cȝ用户Q?/span>user of classQ是谁?E序?/span>programmer?/span>user of class如?/span>user of application?/span>
cȝ用户最兛_啥?最兛_的是接口?/span>
class Screen { public: // interface member functions typedef std::string::size_type index; private: std::string contents; index cursor; index height, width; }; |
注意Q?/span>
typedef是放?/span>public部分的,q样Screen的用户都能够使用q个typedef?/span>
成员函数定义?/span>inline。这个定义即可以在函数声明里Q也可以在类定义之外。在cd明和cd义里把函数指定ؓinline都是合法的。(It is legal to specify inline both on the declaration and definition.Q?/span>
cd义一般要攑֜头文件中?/span>
cd明后Q即使没有类定义Q也可以作ؓ引用或者指针来使用Q?/span>
class LinkScreen { Screen window; LinkScreen *next; LinkScreen *prev; }; |
LinkScreen定义完成之前Q我们就定义了指?/span>LinkScreencd的指针?/span>
q里引入了两个基本感念:
前向声明forward declaretionQ?/span>
class Screen; //definition of the Screen class |
q就是一个前向声明。表C?/span>Screen是一个类cdQ?/span>class typeQ?/span>
不完全类?/span>incompete typeQ在前向声明之后Q类定义之前Screen是一个不完全cd。意思是说知?/span>Screen是个cdQ但是不知道q个cd包括那些成员Q?/span>membersQ。不完全cd只能用在有限的几U方式,它可以用来定义这U类型的指针或者是引用Q也可以用在把这U类cd作ؓ形参或者返回值类型的函数声明。(An incomplete type may be used to define only pointers or references to the type or to declare (but not define) functions that use the type as a paremeter or return type.Q?/span>
在创建某个类cd的对象前Q这个类cd必须完整定义。这L译器能够知道要U类型的对象保留多大的存储空间?/span>
单的_是当我们需要引用整个对象而不是对象中的成员的时候。最常见的情冉|在返回值是被调用的对象的引用的函数里。(when we need to refer to the object as a whole rather than to a member of the object. The most common case where we must use this is in functions that return a reference to the object on which they were invoked.Q?/span>
this是指针,因此如果q回值是当前对象的引用,那么q回语句一定是Q?/span>
return *this; |
刚开始看到这个的时候,L?/span>Java搞Q因?/span>Java是没有指针,所以返回的只能?/span>thisQ显式用的时候也?/span>this?/span>
q里有两个规则:
可以定义两个操作Q一个是constQ另一个不是,q也是重蝲?/span>
class Screen { public: // interface member functions // display overloaded on whether the object is const or not Screen& display(std::ostream &os) { do_display(os); return *this; } const Screen& display(std::ostream &os) const { do_display(os); return *this; } private: // single function to do the work of displaying a Screen, // will be called by the display operations void do_display(std::ostream &os) const { os << contents; } // as before }; |
q样const对象使用const成员函数Q非const对象可以使用M成员函数Q不q最好还是用非const的成员函数?/span>
Ҏ(gu)下面的例子(Z指针形参是否指向const的函数重载)Q?/span>
Record lookup(Account&); Record lookup(const Account&); // new function |
即对象?/span>constQ有时我们也希望其中的某些数据成员是可以修改的,q就引出?/span>mutable?/span>
可变数据成员l不?/span>constQ即使对象是const。这?/span>const的成员函C是可以修改可变数据成员的?/span>
定义Q?/span>
class Screen { public: // interface member functions private: mutable size_t access_ctr; // may change in a const members // other data members as before }; |
构造函C能声明ؓconst?/span>
的方法:使用构造函数初始化?/span>Constructor Initializer?/span>
// recommended way to write constructors using a constructor initializer Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0) { } |
以下是正的写法Q?/span>
// recommended way to write constructors using a constructor initializer Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0) { } |
但是q种写法乍一看似乎和下面不?/span>Constructor Initializer的效果一P
// legal but sloppier way to write the constructor: // no constructor initializer Sales_item::Sales_item(const string &book) { isbn = book; units_sold = 0; revenue = 0.0; } |
NOQ?/span>NOQ不一栗后一U写法那像郭dU相声说的房都烧没了Q就剩下个防盗门Q于谦还开门进厅R?/span>isbn的初始化是在构造函数执行之前完成的。上面这个写法先用缺省的string的构造函数初始化isbnQ在构造函数的函数体执行时Q?/span>isbn又重新赋gؓbook。没效率啊!但是对于内置cd来说Q两U方式无论是在结果上q是在性能上又都是没有区别的?/span>
构造函数的执行分成两个步骤Q?/span>
在计阶D前初始化阶D就开始了。(Initialization happens before the computation phase begins.Q?/span>
在一些特定的情况下必M?/span>Constructor InitializerQ这些特D的情况包括Q?/span>
for example:
// legal but sloppier way to write the constructor: // no constructor initializer class ConstRef { public: ConstRef(int ii); private: int i; const int ci; int &ri; }; // no explicit constructor initializer: error ri is uninitialized ConstRef::ConstRef(int ii) { // assignments: i = ii; // ok ci = ii; // error: cannot assign to a const ri = i; // assigns to ri which was not bound to an object } |
正确写法Q只有用constructor initializer才有Z?/span>const和引用进行初始化?/span>
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { } |
成员的初始化序是成员的定义的序。第一个定义的成员W一个初始化Q然后是下一个,以此cL。(The order in which members are initialized is the order in which the members are defined. The first member is initialized first, then the next, and so on.Q?/span>
Constructor Initializer初始化式使用cȝ某一个构造函数来完成?/span>
// alternative definition for Sales_item default constructor Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {} |
~省构造函?/span>
只有在类没有定义M一个构造函数时Q编译器才会自动生成~省的构造函数?/span>
友元?yu)是允许其它特定的类和方法来讉K它的?/span>public的成员。(The friend mechanism allows a class to grant access to its nonpublic members to specified functions or classes.Q?/span>
友元定义Q?/span>
class Screen { // Window_Mgr members can access private parts of class Screen friend class Window_Mgr; // ...rest of the Screen class }; |
友元不是cȝ成员Q但是他们是cȝ接口的组成部分。(even if they are not members of the class, they are "part of the interface" to the class.Q?/span>
友元可以是:
l c?/span>
l cȝ成员函数
class Screen { // Window_Mgr must be defined before class Screen friend Window_Mgr& Window_Mgr::relocate(Screen::index r, Screen::index c, Screen& s); // ...rest of the Screen class } |
l 一般的非成员函?/span>
友元声明和友元定义之间存在的怺依赖关系使得我们心地构造类。再看上面的例子:
Window_Mgr必须要的Screen前定义。但是成员方?/span>relocate要在Screen定义后才能定义。靠Q这像是先有蛋q是先有鸡一h巴?/span>
class Screen { // Window_Mgr must be defined before class Screen friend Window_Mgr& Window_Mgr::relocate(Screen::index r, Screen::index c, Screen& s); // ...rest of the Screen class } |
q句话是?/span>static class member的最重要的解释:
静态数据成员独立于cȝM对象而存在,每个静态数据成员是和类相关的对象,而不是和cȝ对象相关。(a static data member exists independently of any object of its class; each static data member is an object associated with the class, not with the objects of that class.Q?/span>
l static成员的名字的作用范围是类Q因此可以避免和其它的类的成员或者全局对象的名字的冲突?/span>
l 强制q行装?/span>static成员可以?/span>private的,而全局对象是不可以的?/span>
l 如果?/span>static成员兌到某个特D的cMQ这h于阅M码?/span>
在静态成员函敎ͼstatic member functionQ中是不能?/span>this指针的。这其实很好理解Q因?/span>this指针指向的是对象Q而静态成员函数ƈ不属于Q何的对象?/span>
静态数据成员可以是M的类型:constQ引?/span>referenceQ数l,指针cR?/span>
class Account { public: // interface functions here void applyint() { amount += amount * interestRate; } static double rate() { return interestRate; } static void rate(double); // sets a new rate private: std::string owner; double amount; static double interestRate; //q是声明 static double initRate(); }; // define and initialize static class memberQ这是定?/span> double Account::interestRate = initRate(); |
以上是一个完整的static的定义,要注意以下几点:
1. 静态数据成员要在类定义体之外来定义。(static data members must be defined (exactly once) outside the class body.Q?/span>
2. 静态成员不能通过构造函数进行初始化Q而是在定义的时候就完成了初始化。(static members are not initialized through the class constructor(s) and instead should be initialized when they are defined.Q?/span>
3. 关键?/span>static只是用在cM内的成员声明Q而当在类的外面的寚w态数据成员进行定义时Q就不要再用关键字static。(The static keyword, however, is used only on the declaration inside the class body. Definitions are not labeled static.Q?/span>
a const static data member of integral type can be initialized within the class body as long as the initializer is a constant expression
period的定义就是这P
class Account { public: static double rate() { return interestRate; } static void rate(double); // sets a new rate private: static const int period = 30; // interest posted every 30 days double daily_tbl[period]; // ok: period is constant expression }; // definition of static member with no initializer; // the initial value is specified inside the class definition const int Account::period; |
注意Q虽?/span>period在类定义体内完成了初始化Q但是还是要在类定义之外定义数据成员。(When a const static data member is initialized in the class body, the data member must still be defined outside the class definition.Q?/span>
也正因ؓ如此Q对于非静态成员的非法的用方式,对于静态成员来说就是合法的。例如:静态成员的cd可以是它所属类的类型。非静态数据成员被严格限制只能是它所属类的对象的引用或者是指针。(the type of a static data member can be the class type of which it is a member. A nonstatic data member is restricted to being declared as a pointer or a reference to an object of its class:Q?/span>
class Bar { public: // ... private: static Bar mem1; // ok Bar *mem2; // ok Bar mem3; // error }; |
泛型法
法Z保持独立性,不用容器类型,而?strong>q代?/strong>?/span>
法l不执行容器操作。算法仅仅是Ҏ(gu)q代器和q代器操作来操作的。因此所有会改变q代器的操作在通用法里都不会出现。例如增加或删除元素。但是这些通用法有可能会修改容器中元素的|也可能会在容器中Ud元素?/span>
必须要包含一下的头文Ӟ
#include <algorithm>
#include <numeric>
q个法的参数很好说明了法实际上是不知道元素的cd的。第三个参数指定了v始|因ؓaccumnulate不知道它正在汇ȝ元素的类型?/span>
int sum = accumulate(vec.begin(), vec.end(), 42); |
另一个例子:
string sum = accumulate(v.begin(), v.end(), string("")); |
q里一定要指定起始值是stringQ而绝不能写成字符串常量,因ؓ字符串常量对应的数据cd?/span>const char*?/span>
有趣Q?/span>
需要两对P代器Q?/span>
size_t cnt = 0; list<string>::iterator it = roster1.begin(); // look in roster1 for any name also in roster2 while ((it = find_first_of(it, roster1.end(), roster2.begin(), roster2.end())) != roster1.end()) { ++cnt; // we got a match, increment it to look in the rest of roster1 ++it; } |
roster1?/span>listQ?/span>roster2可以?/span>dequeQ?/span>vectorQ只要我们可以用==操作比较q两个序列中的元素。尤其是Q如?/span> roster1 ?/span> list<string> 对象Q则 roster2 可以?/span> vector<char*> 对象Q因?/span> string 标准库ؓ string 对象?/span> char* 对象定义了相{(==Q操作符?/span>
l 标记范围的两个实参类型必ȝ匹配,必须指向同一个容器中的元素(或者超出容器末端的下一位置Q,q且如果两者不相等Q则W一个P代器通过不断地自增,必须可以到达W二个P代器?/span>
l 带有两对q代器参数。每对P代器中,两个实参的类型必ȝ匹配,但不要求两对之间的类型匹配。特别是Q元素可存储在不同类型序列中Q只要这两序列的元素可以比较卛_?/span>
写容器算法最重要的一Ҏ(gu)必须保证法所写的序列臛_要和被写入的元素数目一样大。(we must take care to ensure that the sequence into which the algorithm writes is at least as large as the number of elements being written.Q就是说如果指定的要写入的序列范围必L有效的,必须大于要写入的元素数目Q就是说不能界?/span>
看个最一目了然的例子是Q?/span>
vector<int> vec; // empty vector // disaster: attempts to write to 10 (nonexistent) elements in vec fill_n(vec.begin(), 10, 0); |
back_inserter
使用back_inserter之所以安全,是因为如果指定的范围界Q它执行的ƈ不是数据修改操作而是数据d操作?/span>
unique
sort
stable_sort
谓词predicates
三类q代器:
l insert iterators插入q代?/span>
l iostream iterators
l reverse iterators反向q代?/span>
inserter是P代器适配器,它和容器l定Q生一个P代器Q这个P代器向绑定的容器插入元素。当我们通过inserterq代器赋值时QP代器插入新的元素。(An inserter is an iterator adaptor that takes a container and yields an iterator that inserts elements into the specified container. When we assign through an insert iterator, the iterator inserts a new element.Q?/span>
又分?/span>3c:
list<int> ilst, ilst2, ilst3; // empty lists // after this loop ilst contains: 3 2 1 0 for (list<int>::size_type i = 0; i != 4; ++i) ilst.push_front(i); // after copy ilst2 contains: 0 1 2 3 copy (ilst.begin(), ilst.end(), front_inserter(ilst2)); // after copy, ilst3 contains: 3 2 1 0 copy (ilst.begin(), ilst.end(), inserter (ilst3, ilst3.begin())); |
istream_iterator:?/span>input stream
ostream_iterator:?/span>output stream
P代器都是模板Templates?/span>Example:
istream_iterator<int> cin_it(cin); // reads ints1 from cin istream_iterator<int> end_of_stream; // end iterator value while(cin_it != end_of_stream) { //do something } // writes Sales_items from the ofstream named outfile // each element is followed by a space ofstream outfile; ostream_iterator<Sales_item> output(outfile, " "); |
++it & it++
q代器加1Q一般前~?/span>1Q返回的是增加后的P代器的引用。后~?/span>1QP代器也加1Q但是返回的是未?/span>1的那个倹{?/span>
大师l出了一?/span>istream_iterator?/span>ostream_iteratorq两UP代器的最l典的应用,输入转输出,有这?/span>sampleq是很容易理解这两个东西Q?/span>
// write one string per line to the standard output ostream_iterator<string> out_iter(cout, ""n"); // read strings from standard input and the end iterator istream_iterator<string> in_iter(cin), eof; // read until eof and write what was read to the standard output while (in_iter != eof) // write value of in_iter to standard output // and then increment the iterator to get the next value from cin *out_iter++ = *in_iter++; |
1. 不能?/span>ostream_iterator中读入数据,同时也不能向istream_iterator写入数据Q?/span>It is not possible to read from an ostream_iterator, and it is not possible to write to an istream_iterator.Q?/span>
2. 一旦给ostream_iterator赋|q个写入的操作就提交了。一旦赋|在后l的操作中就不能修改。此外,每个ostream_iterator只能用作输出一ơ。(Once we assign a value to an ostream_iterator, the write is committed. There is no way to subsequently change a value once it is assigned. Moreover, each distinct value of an ostream_iterator is expected to be used for output exactly once.Q?/span>
3. ?/span>ostream_iterator里没?/span>->操作?/span>
既然P代器基本上可以看做一般意义上的P代器Q那么前面所描述的算法应该同时也适用在流q代器上。于是,针对P代器Q我们也可以q行排序Q比较等法操作?/span>
istream_iterator<int> cin_it(cin); // reads ints from cin istream_iterator<int> end_of_stream; // end iterator value // initialize vec from the standard input: vector<int> vec(cin_it, end_of_stream); sort(vec.begin(), vec.end()); // writes ints to cout using " " as the delimiter ostream_iterator<int> output(cout, " "); // write only the unique elements in vec to the standard output unique_copy(vec.begin(), vec.end(), output); |
从这张图上可以看出普通P代器和反向P代器的区别,另外二者都能够执行自增和自减操作,形式是一LQ但本质是不同的。例如Ş式上都是自增QP代器都会?/span>begin?/span>end方向UdQ但是二者却是反方向的。这样做的最大好处是Q我们能够透明C用算法向前或者向后处理容器?/span>
可以辑ֈjava?/span>StringToken的功能:我喜Ƣ这D代?/span>
// find first element in a comma-separated list string::iterator comma = find(line.begin(), line.end(), ','); cout << string(line.begin(), comma) << endl; |
反向q代器获得最后一个单词:
// find last element in a comma-separated list string::reverse_iterator rcomma = find(line.rbegin(), line.rend(), ','); // wrong: will generate the word in reverse order cout << string(rcomma.base(), line.end()) << endl; |
反向q代器可以代表P代器范围以及q个范围是不对称的事实生一个重要的l论。当我们用一般的q代器对反向q代器初始化或者赋值时Q所得到的P代器所指向的元素和初始g一栗(The fact that reverse iterators are intended to represent ranges and that these ranges are asymmetric has an important consequence. When we initialize or assign a reverse iterator from a plain iterator, the resulting iterator does not refer to the same element as the original.Q?/span>
Nothing to say?/span>
其实是对算法进行了分类Q同时对于算法的命名习惯加以阐述Q看完了Q觉得蟩q也无妨哈?/span>
是针对listQؓ了性能优化Q对于算法,list重新实现?/span>
标准IO库实际上是针对三cLstream的操?/span>
stream |
Header |
Type |
|
console |
iostream |
istream ostream iostream |
提供 char wchar_t cin wcin … |
string stream |
sstream |
istringstream ostringstream stringstream |
|
file |
fstream |
ifstream ofstream fstream |
对于IO对象来说是不能拷贝或赋值的Q因此这些写法都是会Mh的:
ofstream out1, out2; out1 = out2; // error: cannot assign stream objects ofstream print(ofstream); out2 = print(out2); // error: cannot copy stream objectsQŞ参不能是IOcd |
l 形参不能?/span>IOcd。返回g不能?/span>IOcd?/span>
l IO对象也不能保存在vectorq样的集合容器里?/span>
条g状态成员是做什么用的?
是用来详l地描述当前的流Q?/span>streamQ的状态。例如:是否可用Q或者遇C某种Ҏ(gu)的错误。这些状态信息即可以讉KQ也可以讄?/span>
每个对象(stream objectQ都会包含一个成员(memberQ,q个成员通过setstate?/span>clear操作q行理。它的类型是iostate?/span>
每个 IO c还定义了三?/span> iostate cd的常量|分别表示特定的位模式?/span>
strm::badbitQ?/span>badbit 标志着pȝU的故障Q如无法恢复的读写错误?/span>
strm::failbitQ如果出现的是可恢复的错误,如在希望获得数值型数据时输入了字符Q此时则讄 failbit 标志?/span>
strm::eofbitQ是在遇到文件结束符时设|的Q此时同时还讄?/span> failbit?/span>
对于IOcȝ条g状态的操作Q不是查询的,eof(),fail(),bad(),q是赋值操作,clear(),setstate()Q应该都是位操作?/span>
1. The program completes normally. All output buffers are emptied as part of the return from main.
E序正常l束。作?/span> main q回工作的一部分Q将清空所有输出缓冲区?/span>
2. At some indeterminate time, the buffer can become full, in which case it will be flushed before writing the next value.
在一些不定的时候,~冲区可能已l满了,在这U情况下Q缓冲区会在写下一个g前刷新?/span>
3. We can flush the buffer explicitly using a manipulator such as endl, flush, ends.
用操U늬昑ּ地刷新缓冲区Q例如行l束W?/span> endl?/span>
4. We can use the unitbuf manipulator to set the stream's internal state to empty the buffer after each output operation.
在每ơ输出操作执行完后,?/span> unitbuf 操作W设|流的内部状态,从而清I缓冲区?/span>
5. We can tie the output stream to an input stream, in which case the output buffer is flushed whenever the associated input stream is read.
可将输出与输入关联(tieQv来。在q种情况下,在读输入时刷新其兌的输出缓冲区?/span>
Q在每个输出都用endl的结?/span>
关于endl?/span>"n的区别?
如果单的从输Z看,二者肯定是没有区别的。但?/span>endl会刷新输?/span>cout?/span>buffer?/span>
l果是:M读输入流的尝试都首先刷C之绑定的输出的~冲区(bufferQ。:
cin.tie(&cout); cin.tie(0); //break tie to cout, cout no longer flushed when cin is read |
交互pȝ一定要保证输出和输入是l定在一L?/span>
fstream header
l ifstream
l ofstream
l fstream
基本语法格式Q?/span>
// construct an ifstream and bind it to the file named ifile ifstream infile(ifile.c_str()); // ofstream output file object to write file named ofile ofstream outfile(ofile.c_str()); |
注意Q文件名要?/span>C风格的字W串?/span>
l 关闭对象,都不可能改变对象内部的状态?/span>Closing a stream does not change the internal state of the stream object.
truncate
当文件打开时清I文件的所有内容,如果使用q个属性对文g臛_要有写入的权?/span>
// opens in binding it to the given file
ifstream& open_file(ifstream &in, const string &file)
{
in.close(); // close in case it was already open
in.clear(); // clear any existing errors
// if the open fails, the stream will be in an invalid state
in.open(file.c_str()); // open the file we were given
return in; // condition state is good if open succeeded
}
q是一个标准的文g重新l定的代码,包括了以上的注意事项?/span>
字符串流和文件流其实很类伹{独有的是利用字符串流来实现格式化输入/输出?/span>
输出
int val1 = 512, val2 = 1024; ostringstream format_message; // ok: converts values to a string representation format_message << "val1: " << val1 << ""n" << "val2: " << val2 << ""n"; |
输入Q?/span>
// str member obtains the string associated with a stringstream istringstream input_istring(format_message.str()); string dump; // place to dump the labels from the formatted message // extracts the stored ascii values, converting back to arithmetic types input_istring >> dump >> val1 >> dump >> val2; cout << val1 << " " << val2 << endl; // prints 512 1024 |
switch语句我用的的不多,q是习惯写成if-else的Ş式?/span>
switch 中的控制:
It is a common misunderstanding to expect that only the statements associated with the matched case label are executed. However, execution continues across case boundaries until the end of the switch statement or a break is encountered. 存在一个普遍的误解Q以为程序只会执行匹配的 case 标号相关联的语句。实际上Q程序从该点开始执行,q跨?/span> case 边界l箋执行其他语句Q直?/span> switch l束或遇?/span> break 语句为止?/span> |
?/span>2U?/span>switch常见的错误写法:
l case 标号必须是整型常量表辑ּ?/span>
l 如果两个 case 标号h相同的|同样也会D~译时的错误?/span>
Switch内部的变量定义:
对于 switch l构Q只能在它的最后一?/span> case 标号?/span> default 标号后面定义变量?/span>
用作条g判断的变量一定要定义在@环体之外Q?/span>
// repeatedly ask user for pair of numbers to sum string rsp; // used in the condition; can't be defined inside the do do { // ... } while (!rsp.empty() && rsp[0] != 'n'); |
do while 循环不可以采用如下方式定义变量:
// error: declaration statement within do condition is not supported do { // ... mumble(foo); } while (int foo = get_foo()); // error: declaration in do condition |
不过其实自己写代码时Q这个各色的写法一般是不会出现的?/span>
预处理器q定义了其余四种在调试时非常有用的常量:
__FILE__ 文g?/span>
__LINE__ 当前行号
__TIME__ 文g被编译的旉
__DATE__ 文g被编译的日期
断言assert是一U预处理宏,assert 宏就求解条g表达式,如果l果?/span> falseQ?/span>assert 输出信息q且l止E序的执行。如果该表达式有一个非Ӟ例如Q?/span>trueQ|?/span> assert 不做M操作?/span>
量不要使用W号?/strong>q行位运,因ؓW号位的处理和机器有兟?/span>
?/span>XOR的描q真是简单明了,For each bit position, the result is 1 if either but not both operands contain 1; Otherwise, the result is 0.
输入输出标准库重载了UM操作W?/span>>>?/span><<用于输入和输出?/span>
q里面有两个关键词:
l Precedence
Precedence specifies how the operands are grouped.优先U规定的是操作数的结合方?/span>
l associativity
Associativity specifies how to group operators at the same precedence level.l合性规定了h相同优先U的操作W如何分l?/span>
例如Q?/span>
赋值操作符是右兌
ival = jval = kval = lval // right associative (ival = (jval = (kval = lval))) // equivalent, parenthesized version |
数操作W是左关?/span>
ival * jval / kval * lval // left associative (((ival * jval) / kval) * lval) // equivalent, parenthesized version |
让你不得不佩服,Lippmanq样的大师仅仅赋值操作符Q?/span>Assignment OperatorsQ就可以讲出q么多的内容?/span>
l 赋D是叛_联,它的q回值是左操作数Q类型ؓ左操作数的类型(The result of an assignment is the left-hand operand, the type of the result is the type of the left-hand operand.Q?/span>
q样好理解q样的赋D句了Q?/span>
先执?/span>jval = 0;q回值是jval Q再执行ival = jval?/span>
int ival, jval; ival = jval = 0; // ok: each assigned 0 |
l 赋值操作的优先U低?/span>
下面的条件语句返回值是trueQ因为先执行赋D句,q回值是iQ?/span>i大于0Q因此条件语句判断ؓtrue?/span>
if (i = 42) |
下面的条件语句返回值是falseQ因为先执行赋D句,q回值是iQ?/span>i{于0Q因此条件语句判断ؓfalse?/span>
if (i = 0) |
l 重点
前置和后|操作符
l
只在必要的时候才使用后置操作W?/span>
l 区别
前置和后|操作符的返回值是不同的,q是最重要的区别,也是理解的基?/span>
前置操作W:值加1Q再q回。(It increments the value and returns the incremented version.Q?/span>
后置操作W:必须保留原始|Q原始值加1Q返回的l果是未增加的倹{(The postfix operator must store the original value so that it can return the unincremented value as its result.Q?/span>
l 代码
cnt?/span>的返回值是cnt?/span>
vector<int> ivec; // empty vector int cnt = 10; // add elements 10...1 to ivec while (cnt > 0) ivec.push_back(cnt--); // int postfix decrement |
*iter++是*(iter++)Q?/span>++操作的优先高于*Q所以先执行iter++Q这个操作的q回值是iterQ然?/span>iter+1.再执行解引用操作*Q这个解引用操作的操作数是返回的iter?/span>
vector<int>::iterator iter = ivec.begin(); // prints 10 9 8 ... 1 while (iter != ivec.end()) cout << *iter++ << endl; // iterator postfix increment |
Sizeof
Q如果是q回对象?/span>sizeQ那?/span>sizeof的返回值是不是和数据有养I
赋值顺?/span>
l && ?/span> || 操作W计其操作数的ơ序Q当且仅当其x作数实影响了整个表辑ּ的值时Q才计算q两个操作符的右操作数?/span>
&& and || operators specify the order in which their operands are evaluated: In both cases the right-hand operand is evaluated if and only if doing so might affect the truth value of the overall expression.
Q这句话说的真是严}。)
l 一个表辑ּ里,不要在两个或更多的子表达式中对同一对象做自增或自减操作?/span>
Do not use an increment or decrement operator on the same object in more than two subexpressions of the same expression.
new表达式返回的是一个指针,q个指针指向新分配的一个对象?/span>
int *pi = new int; // pi points to dynamically allocated, delete pi; |
但是q样写就不是动态分配的指针
int i; int *pi = &i; delete pi; //因ؓpi指向的是一?/span>local变量 |
后面的写法是不能执行delete表达式的?/span>
?/span>new动态创建的对象一定要执行delete来删除。否则内存就会被耗尽?/span>
大师l了以下的徏议:
l Setting the pointer to 0 after the object it refers to has been deleted makes it clear that the pointer points to no object.Q一旦删除了指针所指向的对象,立即指针置?/span> 0Q这样就非常清楚地表明指针不再指向Q何对象。)
l Reading or writing to the object after it has been deleted. This error can sometimes be detected by setting the pointer to 0 after deleting the object to which the pointer had pointed.Q读写已删除的对象。如果删除指针所指向的对象之后,指针置?/span> 0 |则比较容易检出q类错误。)
l Applying a delete expression to the same memory location twice. This error can happen when two pointers address the same dynamically allocated object. If delete is applied to one of the pointers, then the object's memory is returned to the free store. If we subsequently delete the second pointer, then the free store may be corrupted.Q对同一个内存空间用两?/span> delete 表达式。当两个指针指向同一个动态创建的对象Q删除时׃发生错误。如果在其中一个指针上?/span> delete q算Q将该对象的内存I间q还l自由存储区Q然后接着 delete W二个指针,此时则自由存储区可能会被破坏。)
Arrays and Points
vector?/span>arraycM的地方就是它们都是同一对象cd的集合。不同点?/span>array是固定长度的?/span>iterator之于vector如同指针之?/span>array?/span>
vector数组cd不能是引用(reference4.1 Array
Arrayl classl Character Arraystringstringnull+1.
size_t,vectorsize_type
for (size_t ix = 0; ix != array_size; ++ix) ia[ix] = ix; Pointers are iterators for arrays.指针是用于数l的q代器?/span> pointer holds the address of another object:。具体来_指针保存的是另一个对象的地址?/span> string *sp = &s; // sp holds the address of s *sp里面*代表sp是一个指针?/span> Best practiceQ指针初始化Ӟ如果不能指向具体的地址Q就讄?/span>0Q这样在E序中就可以出指针没有指向一个对象(objectQ?/span> 指针初始化和赋值操作的U束 指针初始化和赋?/span> 只有4cd可以初始化指针和指针赋| 1.gؓ0的常量表辑ּ。不能把M?/span>int赋值给指针Q即使这?/span>int的值是0Q但是可以把值是0?/span>const 或者数?/span>0赋值给指针?/span>NULL定义?/span>0Q所以也可以使用?/span>NULL叫做预处理变量?/span> int *pi = NULL 2.cd匚w的对象的地址 3.另一对象末的下一地址 4.同类型的其它有效的指?/span> void*指针Q这是一个特D类型的指针Q它能够是Q何对象的地址。它?yu)是说明它的值是一个指针,但是指针所指向的对象的cd是不可知的?/span> 因此void*指针只能执行通用的操作: l 指针比较 l 传递给function或者作?/span>function的返回倹{?/span> l 赋值给另一?/span>void*指针 注意*在这里是一?/span>operator?/span>dereference a pointer。解引用操作Q?/span>dereference operatorQ返回指定对象的左?/span>lvalue。因此可以进行赋值操作?/span> 引用和指针的区别Q?/span> 1. 引用L指向对象object。引用必d始化Q但指针是可以仅仅定义,而无需初始化的?/span> 2. 赋|引用的赋g改变引用所l定的对象的|引用是不能重新绑定新的对象的。一旦初始化Q引用L指向同一个对象?/span> 引用Q?/span> int &ri = ival, &ri2 = ival2; ri = ri2; // assigns ival2 to ivalq里ri?/span>ri2q是指向2个不同的地址单元 指针Q?/span> int ival = 1024, ival2 = 2048; int *pi = &ival, *pi2 = &ival2; pi = pi2; // pi now points to ival2Q?/span>pi?/span>pi2都指向了同一个地址单元 指向指针的指?/span>**ppiQ代码: int ival=1024; int *pi = &ival; int **ppi=π int *pi2 =*ppi; //或?/span> int *pi2 = pi; ?/span>ppi解引?/span>2ơ,可以得?/span>ival的g?/span> 当用数l的名字Ӟq个名字p动{换ؓ指向数组W一个单元的指针?/span> int ia[] = {0,2,4,6,8}; int *ip = ia; // ip points to ia[0] 定义ip是指针,赋gؓia 指针的算数运会产生一个新指针?/span> 两指针相减得到的数据cd?/span>ptrdiff_t。它是一?/span> int last = *(ia + 4); // ok: initializes last to 8, the value of ia[4] 如何获得一个数l的l尾Q?/span> const size_t arr_size = 5; int arr[arr_size] = {1,2,3,4,5}; int *p = arr; // ok: p points to arr[0] int *p2 = p + arr_size; // ok: p2 points one past the end of arr p2是指向了数l的l尾?/span> 如果指针指向的是一?/span>const对象Q那么肯定不希望它能够修改这?/span>const对象的倹{那么这L指针如何定义Q?/span> const double *cptr; //cptr是指?/span>const doublecd的指?/span> 对于q个定义的理解是q样子的Q?/span> A. q里的限定词Q?/span>qualifierQ?/span>-const是限?/span>cptr指向的数据类型必Lconst doubleQ而不是限?/span>cptr本n?/span> B. cptr本n不是const C. 不能做的是用cptrM改它指向的对象的?/span> 但是也可以把指向const对象的指针赋gؓ?/span>const对象的地址。(A pointer to a const object can be assigned the address of a nonconst objectQ?/span> int val=10; const int *cptr = &val; val=30; //q样写是对的 *cptr = 30; //q样写会Mh?/span>L 不过我想Q这只不q是代码上的文字游戏而已Q如果程序这样写Q也怼Mh的。乱! q种指针的用途是作ؓҎ(gu)的参敎ͼq可以保证这个参C会在Ҏ(gu)中被修改。(哎,要不怎么说是大师呢,佩服Q) const指针的值是不可以修改的Q但?/span>const指针指向的对象的值是可以修改的?/span> int errNumb = 0; int *const curErr = &errNumb; // curErr is a constant pointer *curErr = 0; // ok: reset value of the object to which curErr is bound 复习Q?/span> typedef 可以用来定义cd的同义词?/span>wages是double的同义词 typedef double wages; // wages is a synonym for double Q以下的q段很饶人啊Q?/span> typedef string *pstring; //pstring?/span>string的同义词Qƈ?/span>pstring是指针类?/span> const pstring cstr; //q是指向string?/span>const指针Q而不是指?/span>const string的指?/span> W一l:pstringcd是指?/span>stringcd的指针类型?/span> W二l:const是用来修?/span>pstringcd的,const pstring是const?/span>string指针 W三l:q不存在单的文字替换的游戏:const pstring cstr;{于const string* cstr;所以就是指?/span>const string的指针。错误! 不徏议在C++中?/span>C风格的字W串Q原因是它有“many many”的安全问题?/span> 它不是一U类型,而是以空字符 null l束的字W数l?/span> 定义Q?/span> char ca2[] = {'C', '+', '+', '"0'}; // explicit null char ca3[] = "C++"; // null terminator added automatically const char *cp = "C++"; // null terminator added automatically char *cp2 = ca2; // points to first element of a null-terminated char array 遍历字符?/span> const char *cp = "some value"; while (*cp) { // do something to *cp ++cp; } C标准库提供了一pd的字W串函数Q但是这些函数都不检查参数的合法性(限制条gQ。这是Lippman不徏议在C++中?/span>C风格的字W串的原因?/span> 主要的限Ӟ 1. 传递给q些标准库函数的指针必须是非I的?/span> 2. 每个指针必须是以nulll尾的?/span> 3. 数组的长度要_?/span> strlen(s)Q返回的?/span>s的长度,但是q里q不包含nullQ因此数l的实际长度?/span>strlen(s)+1?/span> Z么要使用动态数l? 通常是因为在~译时无法知道数l的l数Q所以才需要动态创数组?/span> size_t n = get_size(); // get_size returns number of elements needed int* p = new int[n]; 如果是普通的定义数组Q以上的定义是根本无法实现的Q因为在~译期是不能定数组的大的。只能这样定义: const n=45; int p[n]; 因此可以得出结论:动态数l主要是用于当数l的寸不能在编译期定的情c?/span> 数组释放Q?/span> delete [] p ; 我认为在const size_t len = strlen(pc +1); q行存在W误Q应该是 const size_t len = strlen(pc )+1; 否则copy后得到的l果pc2不是C风格的字W串Q因为最后一个单元的内容?/span>’g’而不?/span>null?/span> const char *pc = "a very long literal string"; const size_t len = strlen(pc +1); // space to // performance test on string allocation and copy for (size_t ix = 0; ix != 1; ++ix) { char *pc2 = new char[len + 1]; // allocate the space strcpy(pc2, pc); // do the copy cout << "pc2=" << pc2 << endl; cout << "pc2[25]=" << (pc2+25) << endl; delete [] pc2; // free the memory } C++实际是没有多l数l的Q?/span> int ia[3][4] 可以理解为数字大是3Q每个单?/span>element又是一个大是4?/span>int数组。或者叫?/span>3?/span>3列?/span> q段代码如?#8220;?#8221;字的N多种写法一P int ia[3][4]={0,1,2,3,4,5,6,7,8,9,10,11}; //*ip是一个指针变量,它指向的是长度是4?/span>int数组?/span> int (*ip)[4]=ia; //*ipo是一个指针数l?/span> int *ipo[4] ; ipo[2]=ia[2]; //Ҏ(gu)针数l的单元赋?/span> //*ipt是一个最普通的指针Q对指针变量赋?/span> int *ipt=ia[2]; // int (*ipn)[4]=ia; ipn=&ia[2]; //也可以写?/span>ia+2 cout << "(*(ip+2))[0]=" << (*(ip+2))[0] << endl; cout << "ipo[2][0]=" << ipo[2][0] << endl; cout << "ipt[0]=" << ipt[0] << endl; cout << "(*ipn)[0]=" << (*ipn)[0] << endl; 打印l果是一LQ都?#8216;8’?/span> 指针数组Q?/span> int *ip[4]; // array of pointers to int 如果从内向外阅读 ip 的声明,则可理解为:*ip ?/span> int[4] cd——即 ip 是一个指向含?/span> 4 个元素的数组的指针?/span> int (*ip)[4]; // pointer to an array of 4 ints q一步的理解Q?/span> int (*ip)[4]=ia; ip=&ia[2]; //也可以写?/span>ia+2 刚开始,我觉得应该写?/span>ip=ia[2];l果得到报错信息Q?/span> error: cannot convert `int[4]' to `int (*)[4]' in assignment 因ؓia[2]是一个数l啊Q再惛_果这样写ip=ia[2];q就相当?/span> int ia[4]= {0,1,2,3}; int *ip=ia; ip=ia[2]; 当然是错误的?/span>
4.2 Pointers
什么是指针
Operations on PointerQ指针操作)
1. 使用指针讉K数组单元
指向const对象的指针和const指针
1. 指向const对象的指?/span>
2. const指针
3. 指针?/span>typedef
4.3 C风格字符?/span>
1. 什么是C风格的字W串Q?/span>
2. 字符串函?/span>
3. 使用动态数l?/span>
4. 疑问代码Q?/span>P139Q:
4.4多维数组 Multidimensioned Arrays
1. 指针和多l数l?/span>
2. 以下2U定义的区别Q圆括号是必不可的Q?/span>
]]>
Library Types
Z么要?/span>namespaceQ?/span>
namespace是Z区分重名的类?/span>
名字I间的主要作用是解决标识W冲H问题?/span>
比如C++ Standard Library是在名字空?/span>std下,如果没有名字I间Q其中的list非常可能和自己定义的一些标识符重复?/span>
两种使用Ҏ(gu)Q?/span>
1?/span> std::list<int> myIntList;
2、在cpp文g的开头加?/span>using namespace std;
q样在文件中可以直接?/span>list<int> myIntList了?/span>
初始化:
string s1; string s2(s1); string s3("value"); string s4(n, 'c'); |
dstring对象Q有两种方式
1. 使用d操作W?/span>cin
cin >> s1 >> s2; |
2. 使用getline()函数
getline(cin, line) |
输入操作W会忽略开头的I格Q直至遇到第一个空白字W(leading spaceQ就停止d。和输入操作W不一L是,getline() q不忽略行开头的换行W?/span>
String.size()q回的不?/span>int或?/span>unsigned数据cdQ而是一个和机器无关?/span>string::size_typeQ虽然不知道string::size_type的具体类型,但可以肯定的是它一定是unsignedcd?/span>
string st1, st2 = "The expense of spirit"; st1 = st2; // q是两个对象Q?/span>replace st1 by a copy of st2 |
更加关键的是st1 = st2;的执行过E:
1实际上是把原来分配给st1的空间删除掉Q?/span>
2再分配新的空_copyq来st2的内宏V?/span>
vector中包含的必须是统一的数据类型?/span>
vector不是一U类型,q是一个模?/span>template?/span>
vectorq不需要预先分配内存空间?/span>
初始化:
vector<T> v1; vector<T> v2(v1); vector<T> v3(n, i); vector<T> v4(n); |
vector的大?/span>
vector<int> ivec(10,1); vector<int>::size_type size = ivec.size(); |
tipQ?/span>
如果要取?/span>vector的大,要定义数据类型是vector<int>::size_type。这?/span>Java有很大的不同Q随意性很了?/span>
每个容器cd都会定义自己的P代类型?/span>
vector<int>对应的P代类型就?/span>vector<int>::iterator?/span>
每个容器cd中都会包含一个成员(memberQ,q个成员是容器的q代器的实际cd?/span>
只要一U类型实Cq代器所定义的所有操作,那么q种cd可以称为是一个P代器cdQ?/span>iterator typeQ。因此P代器cd有很多种?/span>
每种容器cd都定义了对应?/span>iteratorcdQ但是ƈ不是所有的容器cd都定义下表烦引的方式。因此应使用容器讉K容器的单元?/span>
讉K当前单元的D使用解引用操作符*Q解引用操作W返回的是一个倹{?/span>
for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter) *iter = 0; //set element to which iter refers to 0 |
const_iterator
我刚开始不是很明白Z么要定义const_iteratorcdQ后来看了下面的代码 – 以只L式遍历一?/span>vectorQ就明白?/span>
定义Q?/span>const vector<int>::iterator it 遍历Q?/span>++itQ?/span> |
遍历Q?/span>++it //错误Q?/span>
所以要q样定义Q?/span>
vector<int>::const_iterator it
遍历Q?/span>++it // ok
// an iterator that cannot write elements vector<int>::const_iterator // an iterator whose value cannot change const vector<int>::iterator |
M操作改变?/span>vector?/span>sizeQ那么现有的iterator都会失效。就是说Q?/span>
先执?/span>vector<int>::iterator iter = ivec.begin()
然后q加elementQ?/span>ivec.add(9);
那么前面?/span>iter失效了?/span>
首先明确q和vector一P都是模板template?/span>bitset只有大小的不同?/span>
bitset<32> bitvec; // 32 bits, all zero
bitset初始?/span>
bitset<n> b; bitset<n> b(u); bitset<n> b(s); bitset<n> b(s, pos, n); //b ?/span> string 对象 s中含有的位串的副本,?/span>pos位置截取n?/span>bit |
从构造函数可以看出,bitset是指定长度的Q这?/span>vector有点不一栗?/span>
?/span>string初始?/span>bitsetQ要注意的是string?/span>bitset的顺序是相反的:是说从叛_左读string?/span>bit?/span>
例如Q?/span>
string strval("1100"); bitset<32> bitvec4(strval); |
那么bitvec4的第2位,W?/span>3位才?/span>1.即是对string的截串处理也应保?strong>从右向左?/strong>?/span>