《Primer C++ 3rd》读书笔记2

来源:互联网 发布:汉娜·穆雷 知乎 编辑:程序博客网 时间:2024/05/05 03:27

第二章

这一章使用了数组来介绍标准C++。

内置数据类型built-in data types-->符合类型compound types-->标准库类型standard library class types-->准标准库/第三方库类型-->自定义类型
 
数组的索引,0来表示第一个元素的索引。注意不要出现一位偏移错误off-by-one。

数组是C++对C的继承,体现了C的算法与数据分离的过程化语言的特点,它不是first-class数据类型。

这里面输了array的不足:不支持数组间负值,无法比较大小,数组间无法比较,通过循环初始化,容易出现索引溢出。

动态和静态内存分配的不同:The two primary differences between static and dynamic memory allocation are as follows.

Static objects are named variables that we manipulate directly, whereas dynamic objects are unnamed variables we manipulate indirectly through pointers. We'll see an example of this in a moment.

The allocation and deallocation of static objects is handled automatically by the compiler; the programmer needs to understand it but need not do anything about it. The allocation and deallocation of dynamic objects, in contrast, must be managed explicitly by the programmer and, in practice, is considerably more error-prone. It is accomplished through the use of the new and delete expressions.

动态分配内存,new只提供一个指针,可以分配数组,但分配数组时不可以负初值。必须要及时使用delete/delete []释放内存。

(c) int *pi2 = new int( 1024 );
(d) int *pi3 = new int[ 1024 ];
看清楚,这两个可不一样的,打代码的时候要小心。

新类代表一种新的数据类型,这样想很方便,所以可以定义类的对象和指向这一类的对象的指针。A class name represents a new data type. We can use it to define objects of our class type in the same way we use the built-in types to define those objects. For example:

// a single IntArray class object
IntArray myArray;

// a pointer to a single IntArray class object
IntArray *pArray = new IntArray;

类定义要包括class关键字,类名,和类体,前两者用于声明类。

使用信息隐藏的好处:This division between the public interface and private implementation of a class is referred to as information hiding. Information hiding is an important software engineering concept that we look at in more detail in the later chapters of this text. Briefly, it provides two primary benefits for our programs.

If the private implementation of the class needs to be modified or extended, only the relatively small number of member functions requiring access to that implementation needs to be modified; the many user programs making use of our class generally do not need to be modified, although they may require recompiliation (we illustrate this in Section 6.18).

If there is an error in the state of the private class implementation, the amount of program code we need to examine in general is localized to the relatively small number of member functions requiring access to that implementation; the entire program need not be examined.

由于C++不允许成员函数和数据成员通明,所以当需要用共有成员函数包裹私有数据成员的时候,通常在数据成员前使用_来进行区分。

对于C++面向对象编程中get/set包裹私有数据的方法都要直接使用inline函数,这样不会造成性能下降,也就是说在声明的时候就做好这份那个面的事情。

initialize array_size to the dimension of the array. The first instance, however, appears to require a function call, whereas the second involves only a direct memory access. Generally, a function call is significantly more expensive than a direct memory access. So does information hiding impose a significant expense梡erhaps a prohibitive expense梠n the run-time efficiency of a program? Happily, in general, the answer is no.

The solution provided by the language is the inline function mechanism. An inline function is expanded in place at its point of call. In general, that is, an inline function does not involve any function call at all.[1] For example, the call of size() within the condition clause of the for loop

for ( int index = 0; index < array.size(); ++index )
      // ...
is not actually invoked _size times but instead is inline-expanded during compilation into the following general form:

// general inline expansion of array.size()
for ( int index = 0; index < array._size; ++index )
      // ...
A member function defined within the class definition, such as size(), is automatically treated as an inline function. Alternatively, the inline keyword can be used to request that a function be treated as inline

构造函数与类名通名,且没有返回值和返回类型声明。

C++编译器通过函数原形来唯一区分函数,就是说函数名,返回类型,参数的数量和参数类型。

通过static来实现在类的所有对象之间共享数据。

class IntArray {
public:
   // ...
private:
   void init( int sz, int *array );
   // ...
};

void
IntArray::
init( int sz, int *array )
{
   _size = sz;
   ia = new int[ _size ];
  
   for ( int ix=0; ix < _size; ++ix )
         if ( ! array )
              ia[ ix ] = 0;
         else ia[ ix ] = array[ ix ];
}

Our three rewritten constructors are as follows:

IntArray::IntArray( int sz ){ init( sz, 0 ); }
IntArray::IntArray( int *array, int sz )
      { init( sz, array ); }
IntArray::IntArray( const IntArray &rhs )
      { init( rhs.size, rhs.ia ); }
注意这一段IntArray::IntArray( int sz ){ init( sz, 0 ); }使用0给指针array付值,来完成条件判断。指针可以付给0值,使特殊情况。

B从A继承而来,则B使特殊的A。所以如下代码可以的。
#include <IntArray.h>
void swap( IntArray &ia, int i, int j )
{
    int tmp = ia[ i ];
    ia[ i ] = ia[ j ];
    ia[ j ] = tmp;
}

Here are three legal invocations of swap():

IntArray       ia;
IntArrayRC     iarc;
IntSortedArray ias;

// ok: ia is an IntArray
swap( ia, 0, 10 );

// ok: iarc is a subtype of IntArray
swap( iarc, 0, 10 );

// ok: ias is also a subtype of IntArray
swap( ias, 0, 10 );

// error: string is not a subtype ...
string str( "not an IntArray!" );
swap( str, 0, 10 );

注意::和:的不同,前者表示函数与类的关系。A::B的话A为类,B为函数,前面包含后面;后者是定义继承和初始化表的时候使用的,A:B则B是A的根本,后者包含前者。。。这个老记不清。。。
class IntArrayRC : public IntArray
inline IntArrayRC::IntArrayRC( const int *iar, int sz )
     : IntArray( iar, sz ) {}

当定义一个继承类的对象的时候,首先会分配基类的内存,然后调用积累的构造函数初始化,之后分配继承类内存,调用继承类初始化表。子类不继承父类的构造函数,所以即便子类没有比父类多加入数据对象,也要通过初始化列表,将定义对象时候的数据初值传递给父类。
inline IntArrayRC::IntArrayRC( const int *iar, int sz )
     : IntArray( iar, sz ) {}
The portion of the constructor marked off by the colon is referred to as a member initialization list. It provides the mechanism by which the IntArray constructor is passed its argument. The bodies of both IntArrayRC constructors are null, because the job of the constructors is only to pass their arguments to the associated IntArray constructor.

如果一个class需要一个copy constructor,或一个copy assignment operator,或一个destructor,那么它也可能学要他们中的其他三个。

Exercise 2.13
Given the following type declarations

template <class elemType> class Array;
enum Status { ... };
typedef string *Pstring;

which, if any, of the following object definitions are in error?

(a) Array< int*& > pri( 1024 );
(b) Array< Array<int> > aai( 1024 );
(c) Array< complex< double > > acd( 1024 );
(d) Array< Status > as( 1024 );
(e) Array< Pstring > aps( 1024 );
这道题和本章后面几道题以后要仔细看。

真是的,怎么在第二章就出这种提了,头大,留着以后作罢。
Exercise 2.15
Given the following class template

template <class elemType>
class Example2 {
public:
   explicit Example2( elemType val = 0 )
             : _val( val ){}
            
   bool min( elemType value )     { return _val < value; }
   void value( elemType new_val ) { _val = new_val;      }
   void print( ostream &os )  { os < _val;          }
private:
   elemType _val;
};

template<class elemType>
ostream& operator<( ostream &os, const Example2<elemType> &ex )
        { ex.print( os ); return os; }

what happens when we write the following?

(a) Example2< Array<int>* > ex1;
(b) ex1.min( &ex1 );
(c) Example2< int > sa( 1024 ), sb;
(d) sa = sb;
(e) Example2< string > exs( "Walden" );
(f) cout < "exs: " < exs < endl;

异常处理机制:
1、 程序中出现异常的地方。用throw抛出,等待处理。此时程序被挂起。
2、 程序中的异常被处理的地方。用try...catch组和语句来实现。通过program callback stack来逐层跳出,直到找到相应的catch位置,否则,函数将一直挂起至main(),然后按章标准调用terminate()结束。

namespace用来防止标识符的污染。使用别名来简化较长的namespace,同时可以实现替换。

郁闷!好不易手动在vs6sp6和CBX1上打出了最后一道题的官方答案,以作练习,没想到编译通过后竟然是terminate。。。也不知道哪里有的问题。。。毕竟还是新手。。。
不过自己打一边就适合用眼睛看得不一样,所谓眼高手低呀,发现好多低级错误。。。
这里把这段程序贴上,用作以后研究。
练习2.23
#include <vector>
#include <iostream>
using namespace std;

template <class elemtype>
elemtype min1(const vector<elemtype> &vec)
        {
        elemtype minimum;
if (vec.size()>-1) minimum=vec[0];
else throw "empty vector -index.";
        for(int i=1;i<vec.size();i++)


                if(vec[i]<minimum){
                        minimum=vec[i];
                        return minimum;}
        }

template <class elemtype>
elemtype min2(const vector<elemtype> &vec)
{
  vector<elemtype>::const_iterator iter=vec.begin();
  elemtype minimum;
  if(iter<vec.end()) minimum=*iter;
else throw "empty vector -iterator";
for(++iter;iter<vec.end();++iter)

  if(*iter<minimum) minimum=*iter;
  return minimum ;
}
  int main(){
    int array[]={9,4,5,3,7,8,0,7,2,1};
vector<int> a (array,array+10);
cout<<"should be 0:"<<min1(a)<<endl;
cout<<"should be 0:"<<min2(a)<<endl;
vector<int> b (array,array+9);
cout<<"should be 1:"<<min1(b)<<endl;
cout<<"should be 1:"<<min2(b)<<endl;
vector<int> c;
try{
  cout<<"should be 1:"<<min1(c)<<endl;
  cout<<"should be 1:"<<min2(c)<<endl;
}
catch(char *s){
  cerr<<"exception"<<s<<endl;
}
 return 0;
}
对了,CBX中使用的gcc还显示了黄色的疑问:
"ch2-1.cpp": ch2-1.cpp `typename std::vector<elemtype, std::allocator<_CharT> at line 26
>::const_iterator' is implicitly a typename
"ch2-1.cpp": ch2-1.cpp implicit typename is deprecated, please see the at line 26
documentation for details

VS中没有任何显示的问题提示。
还有显示疑问的第26行是:vector<elemtype>::const_iterator iter=vec.begin();
不知道是怎么回事。。。
我觉得也不像是try那里出了问题。。。反正是很麻烦。这不vs6直接就刮掉了,连带高的几个其他的软件也开始泛毛病了。。。提前领教了编程的痛苦。为以后做好了现实的思想准备。。。

btw,CBX1的确有很多不错之处,但是麻烦也比较多。可能也是因为java本地编译的兼容性问题吧,最讨厌的就是字符的对位问题了,简直太变态了!!!还有一个讨厌,就是无法使用鼠标中间的键/滚轮,比较麻烦,不过好像java都这样。。。兼容性呀。。。