c++ primer 习题笔记

来源:互联网 发布:手机安卓版通达信软件 编辑:程序博客网 时间:2024/04/30 05:18

 

1.       爲什麽內建的數組不支持數組和數組之間的賦值操作(assignment)?如果要支持這個操作,需要哪些信息?
數組名代表著一個常量指針;c++語言并未支持數組的賦值操作。編譯器必修在執行時期知道數組的長度,才能產生程序代碼,支持數組對數組的賦值操作。
2.       第一級的數組應該支持什麽樣的操作(operations)?
C++并未支持數組的抽象性(abstraction);它也未支持我們可能希望對數組的所有操作,例如數組間的賦值(assignment),兩個數組之間的比較操作(comparison),或是對於數字大小的詢問操作;即是說,第一級數組的支持應當允許我們將數組視為一個單元,同時又允許我們處理個別的元素。
第一級數組應該支持的操作包括:
  1. 數組初始化(initialization)
  2. 數組賦值(assignment)
  3. 數組間的比較(comparison)
  4. 數組大小的查詢
  5. 索引範圍的檢查
3. int ival =1024;
   對象ival的內存分配屬性是static或是automatic。這個定義要求編譯器分配足夠的內存空間來放置任何類型為int的數值,并將該空間命名為ival,然後將數值1024放入該空間種。
   int *pi = &ival;
   對象pi是個指針,可以保存一個int的內存地址。取地址操作符&返回int對象ival的地址,所以pi是一個指向int的指針,它被初始化為int對象ival的地址。
   int *pi2 = new int(1024);
   對象pi2是個指針,可以保存一個int的內存地址,new操作符分配率一個類型為int的無具體名稱對象,并將它初始化為1024,然後返回其內存地址,於是pi2以該地址作為初值。
   int *pi3 = new int[1024];
   對象pi3是個指針,可以保存一個int的內存地址,new操作符分配了一個擁有1024個整數元素的數組(但是每一個元素都沒有被初始化),并返回第一個元素的內存地址。於是pi3以這一個元素的地址作為初值。
   其中對象ival的類型為int,可以保存“能夠被涵蓋于底層機器所支持的int內”的任何正值或是負值。
3.       int *pi = new int( 10 ); //將一個值為10的無具名int對象的地址保存到一個名為pi的int指針中;
int *pia = new int[ 10 ]; //創建一個有十個未初始化int元素的數組,并把其第一個元素的地址存儲到一個新創建的名為pia的用來存儲int的內存地址的指針中。。。orz
4.       int *pi = new int( 10 );
int *pia = new int[ 10 ];
while( *pi < 10 )
{
 pia[ *pi ] = *pi;
 *pi = *pi + 1;
}
delete pi;
delete [] pia;
這段程序企圖將pia所指的一個由int元素組成數組pia[]加以初始化。pi指向一個無具名的int對象,初值為10,while循環所使用的措施將pi所指的對象拿來和10進行比較,只要小於10,就開始執行循環體。問題是第一次測試就失敗了,於是循環體一次也沒有執行起來。一個可能的解決方案為:
int *pi = new int( 10 );
int *pia = new int[ 10 ];
int i = 10;
 
while( i < 10 )
{
 pia[ *i ] = *i;
   *i = *i + 1;
}
//好像pi就沒有用了。。。。。。。。囧
5.       c++ class的一個關鍵性質就是“接口”與“實現”分離。所謂接口就是一組操作行為,用戶可以將它施行于次class所派生的任何對象身上。它由三個部分構成:操作名稱、返回值、參數列表。一般而言class用戶只需要知道這些就够了。至於底層的實現內容,由算法(algorithms)和數據構成。觀念上,雖然class的接口可以成長,卻不可以改頭換面變得與過去版本不兼容。至於私下實現的內容,可以自由演化。
class Matrix
{
 public:
              //default constructor
               Maxtrix( int = defaultRowSize, int = defaultColumnSize );
 
               //copy constructor
               Maxtrix( const Matrix & );
 
               //destructor
               ~Maxtrix();
 
               //copy assignment operator
               Matrix &operator=( const Matrix & );
 
               //equality operator
               bool operator==( constr Matrix & ) const;
 
               //inequality operator
               bool operator!=( const Matrix & ) const;
 
               //parenthesis operator
               int &operator()( int, int);
 
        //copy parenthesis operator?
        int operator()( int, int ) const;
       
        //Get Row Size
        int getRowSize() cosnt;
 
        //Get Column Size
        int getColumnSize() const;
 
//friends…
 
        //iostream output operator
        friend ostream &operator<<( ostream &, const Matrix & );
 
        //iostream input operator
        friend istream &operator>>( istream &, Matrix & );
 
private:
        //implementation….
        int **im;
 
        //how many rows
        int rows;
 
        // how many columns
        int cols;
 
        // default rows
        static const int defaultRowSize = 5;
 
        // default columns
        static const int defaultColumnSize = 5;
};
6.       constructor(構造函數)和destructor(析構函數)
int main()
{
 IntArray myArray( 1024 );
 //….
   return 0;
}
中,myArray Data Memebers所需要的內存,在constructor被調用之前就分配好了。事實上,編譯器將程序代碼暗中轉換為(以下為不合法的c++代碼):
int main()
{
      IntArray myArray;
 
// c++偽代碼::調用constructor
myArray.IntArray::IntArray( 1024 );
//……..
// c++偽代碼::調用destructor
myArray.IntArray::~IntArray();
 
return 0;
}
class的constructor主要用來為class object的data memebers 設定初值,而destructor的主要用途是釋放class object 生命期間分配到的任何資源。
constructor如下:
// default contructor
Matrix( int = defaultRowSize, int = defaultColumnSize );
 
// constructor
Matrix( int, int, int * );
 
// copy constructor
Matrix( const Matrix & );
我們需要一個destrucotr,因為Matrix有個指針形式的data member(如果class需要一個copy constructor,一個copy asignment operator,或是一個destructor,它大約就同上需要三者)。如果class內至少有一個指向某塊heap內存(堆內存?)的指針,該內存在class contructor內分配,需由destructor釋放,那么該class就需要上述三個函數。
7.       c++會對class object的賦值操作(assignment)提供預設支持,但是預設行為往往不恰當。
在Matrix類中加入:
Matrix &operator=( const Matrix & );
private:
// init with a row
void initMatrix( int * );
 
// init with a matrix
void initMatrix( int ** );
 
void initData( int r, int c );
下面為default constructor的代碼:
Matrix::Matrix( int r, int c )
{
 initData( r, c );
initMatrix( static_cast< int * >(0) );
}
 
第二個constructor:
Matrix::Matrix( int r, int c, int *array )
{
 initData( r, c );
 initMatrix( array );
}
下面為copy constructor:
Matrix::Matrix( const Matrix &rhs )
{
 initData( rhs.rows, rhs.cols );
 initMatrix( rhs.im );
}
 
以下兩個private member functions initMatrix()十分相似:
void Matrix::initMatrix( int *array )
{
 for( int i = 0; i < rows; ++i )
 {
        for( int j = 0; j < cols; ++j )
        {
         if( array )
         {
     im[i][j] = *array++;
 }
 else
 {
     im[i][j] = 0;
}
}
}
}
 
void Matrix::initMatrix( int **matrix )
{
 for( int i = 0; i < rows; ++i )
 {
        for( int j = 0; j < cols; ++j )
        {
               im[i][j] = matrix[i][j];
}
}
}
以下為private member function initData(),它被constructor調用:
void Matrix::initData( int r, int c )
{
rows = r;
cols = c;
 
im = new int *[rows];
for( int i = 0; i < rows; ++i )
{
     im[ i ] = new int[ cols ];
}
}
下面是destructor:
Matrix::~Matrix()
{
 for( int i = 0; i< rows; ++i )
 {
         delete [] im[i];
}
delete [] im;
}
下面為copy assignment操作符:
Matrix &Matrix::operator=( const Matrix &rhs )
{
        if( this != &rhs )
        {
               if( ( rows != rhs.rows) || ( cos != rhs.cols ) ) //!!!!!!!!!兩者容量不等時的處理
               {
       //deallocate old matrix
       Matrix::~Matrix();
       initData( rhs.rows, rhs.cols );
}
initMatrix( rhs.im );
}
return *this;
}
8.       type/subtype繼承體系,通常反應出is-a的關係:ArrayRC是一種Array,AudioBook是一種book。。
// vehicle交通工具; geometry幾何形狀
9.       與類型相依(type-dependent)
10.   protected級別允許derived classes直接訪問base class members,違反封裝概念,但是如果不如此那么derived class的實現代碼就沒有足夠的效率;如果沒有關鍵詞protected,設計者就會被迫將base class members 設成public。在高純度的封裝與實際效率之間,protected訪問級別提供了一個良好的折中方案。
11.   萬一class設計者沒有辨識出應該成為虛擬函數的函數,derived class設計者就沒有辦法改寫(override)這個必要的函數了。虛擬函數比非虛擬函數缺乏效率,由於它們沒有辦法做成inline函數(inline的行為發生於編譯時期,而虛擬函數在執行時期才獲得解析),可能成為程序缺乏效率的罪魁禍首。一旦class擁有一個虛擬函數,該class的每個對象都將有一個虛擬指針(virtual pointer)指向一個虛擬表格(virtual table),其中含有該class的所有尋函數地址(每個對象有一個vptr,每個class有一個vtbl),因此,讓所有member functions都成為虛擬函數,可能影響效率,因為所有member function都需要付出動態分派(dynamic dispatch)的成本,另一個重點是虛擬函數無法inlined。所以,基於效率因素,只有在某些函數必須是virtual時,我們才將它聲明為virtual。
12.   Shapes
首先確認此抽象性的繼承體系:
        Shapes為base class
        Derived classes 可以包括 Rectangle,Circle & Square.
        class Square 是派生自Rectangle的子類型,因為square是一種特殊的rectangle
為此層次體系指定一個小型的public接口,其中包括constructors。
       Shape的public接口:
               class Shapes
               {
       public:
              Shapes();
              virtual ~Shapes();
              virtual void draw();
              virtual void rotate( float );
       protected:
              //…
};
       Rectangle的public接口:
              class Rectangle::public Shapes
              {
                     public:
                            Rectangle( float width, float height );
                            Rectangle( const Rectangle & );
                            virtual ~Rectangle();
                            Rectangle &operator=( const Rectangle & );
                            virtual void draw();
                            virtual void rotate( float );
                     protected:
                            // …
};
       Circle的public接口:
              class Circle::public Shapes
              {
       public:
              Circle( float radius );
              Circle( const Circle & );
              virtual ~Circle();
              Circle &operator( const Circle & );
              virtual void draw();
              virtual void rotate( float );
       protected:
              // …
};
       Square的public接口:
              class Square::public Rectangle
              {
       public:
              Square( float side );
              Square( const Square & );
              virtual ~Square();
              Square &operator=( const Square & );
              virtual void draw();
              virtual void rotate( float );
       protected:
              //…
};
確認哪些函數應該是virtual。
虛擬函數前應該加上關鍵詞virtual;父類的virtual函數對於整個繼承樹而言也都是virtual,子類中可以不必聲明為virtual,不過如果子類也做此聲明,對那些只看到derived class的人有所幫助。
Shapes *ps;     //一個指向shpaes的指針
ps = new Rectangle;       //改為指向一個Rectangle
ps->draw();    //繪製一個Rectangle
ps->rotate( 90. );    //旋轉90度
Shapes *qs = new Rectangle;
*qs = *ps;       //複製Rectangle….err…just code it like this…may be it can,t work..
qs->draw();    //繪製副本
delete ps;
delete qs;
13.   已知如下聲明
template <class elemType>
class Array;
enum Status { … };
typedef string *Pstring;
  1. Array< int *& > pri( 1024 ); 類型int *&是一個reference,代表一個指針。reference必須在定義之際初始化;所以這是一個錯誤的對象定義。
  2. Array< Array<int> > aai( 1024 );對象aai是一個Array,其元素為Arrays,後者的元素為ints。
  3. Array< complex< double > > acd( 1024 );對象acd是一個由complex(複數)組成的Array,複數的虛實兩部的類型均為double。
  4. Array< Status > as( 1024 );對象as是一個Array,其元素類型為Status的枚舉值(enumerators);
  5. Array< Pstring > aps( 1024 ); 對象aps是一個Array,其元素都是指針,指向string。
14.   class example1
{
public:
        example1( double min, double max );
        example1( const double *array, int size );
 
        double& operator[]( int index );
        bool operator==( cosnt example1& ) const;
       
        bool insert( const double*, int );
        bool insert( double );
 
        double min() const { return _min; }
        double max() const { return _max; }
       
        void min( double );
        void max( double );
 
        int count( double value ) const;
 
private:
        int size;
        double *parray;
        double _min;
        double _max;
};
 
下面是改寫為class template:
template < class elemType >
class example1
{
public:
        example1( const elemType &min, const elemType &max );
        example1( const elemType *array, int size );
 
        elemType &operator[]( int index );
        bool operator==( const example1 & ) const;
 
        bool insert( const elemType *, int );
        bool insert( const elemType & );
 
        elemType min() const { return _min ;}
        elemType max() cosnt { return _max; }
        void min( const elemType & );
        void max( cosnt elemType & );
 
        int count( const elemType &value ) const;
 
private:
        int size;
        elemType *parray;
        elemType _min;
        elemType _max;
};
 
  1. constructors:
example1( double min, double max );
->example1( const elemType &min, const elemType &max );
example1( const double *array, int size );
->example1( const elemType *array, int size );
或者說
example1<elemType>( const elemType &min, const elemType &max );
example1<elemType>( const elemType *array, int size );
這是因為參數已由double改變為elemType。這些constructors的名稱變成example1<elemType>,以便允許編譯器針對類型elemType產生一個特定的constructor。
原本的操作符:
double &operator[]( int index );
->elemType &operator[]( int index );
其它member functions的改變十分類似。
       data members中凡類型為double者,都改變為elemType。size的類型仍然為int。
void min( double );->void min( const elemType & );而不是void min( elemType );因為以by value 的方式傳遞內建類型是一種常見而可接受的做法,但是我們應該避免以by value的方式傳遞對象。如果我們以by value 的方式傳遞一個elemType對象,編譯器會調用一個copy constructor以求為該對象製造一份副本,然後才將該副本以by value方式傳遞。所以改為以by const reference的方式傳遞~~ so, const needed….
15.   已知:
templage <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;
}
 
a.       Example2< Array<int >* > ex1; ex1為一個Example2對象,用來管理一個指針,該指針指向一個以ints為元素的Array。
b.       ex1.min( &ex1 ); 無法編譯,因為沒有任何機制可以將Example2< Array<int>* >*轉換為Array<int>*,後者將是min()的參數類型。
c.       Example2< int > sa( 1024), sb; sa與sb均為Example2 對象,其內元素類型為int,sa被初始化為1024,sb被初始化為默認值0.
d.       sa = sb; sa獲得了sb的一份副本
e.       Example2< string > exs( “Walden” ); exs是一個Example2對象,其內之元素類型為string。此對象被初始化為”Walden”。
f.        cout << “exs: “ << exs << endl; 這個會打印出 exs: Walden
16.   在上述Example2定義中,
explicit Example2( elemType val = 0 ) : _val( val ) {}
意圖指定一個默認值。so,Example2<Type> ex1(value); OR Example2<Type> ex2;
然而,代碼使得Type局限於那些“可以被合法初始化為0”的類型(eg.string object初始化為0就不合法)。 同樣,如果Type不支持插入操作符,我們對print()的調用會失敗。如果Type不支持less-than操作符,我們對min()的調用也會失敗。
語言不應該支持所謂的Type_constraint(類型約束)語法:
a.       它會使templates更複雜
b.       無論如何用戶總能夠檢驗Type是否提供了“得以順利將此template實例化”的必要函數;如果他們遺漏了,編譯器會對此實例化行為發出錯誤訊息,指出某個函數有所謂的隱式約束( implicit constraint),而不是只發出錯誤信息說明這個template的實例化是錯誤的。
17.   int *alloc_and_init( string file_name )
{
        ifstream infile( file_name );
int elem_cnt;
infile >> elem_cnt;
 
int *pi = allocate_array( elem_cnt );
 
int elem;
int index = 0;
while( cin >> elem )
{
       pi[ index++ ] = elem;
}
       sort_array( pi, elem_cnt );
       register_data( pi );
 
return pi;
}
以下錯誤可能出現在此函數中:
  1. file_name 可能為空字符串(empty string)
  2. ifstream constructor可能無法打開文件,即便file_name 為一個有效字符串。
  3. 文件起始處可能并不含有一個int value
  4. elem_cnt可能受到一個不太正確的值,太大,或為0,或為負。。
  5. allocate_array()為elem_cnt個元素分配空間時可能失敗
  6. while循環獨到太多元素。這個循環會在遇到EOF或者讀取int失敗時結束,但是它并未考慮到pi所指數組的大小。
  7. sort_array()會對pi所指向的數組排序,該數字假象有elem_cnt個元素,但是while循環可能在讀入elem_cnt個元素之前就結束了,因為輸入端的ints個數不足,并因而造成數組的剩餘部分沒有初值
  8. sort_array()可能會收到一個無效的指針pi,因為此處并沒有檢查allocate_array()的返回值;這個錯誤可能導致程序在循環內當掉。
18.   以下所列函數由上述alloc_and_init()調用。如果失敗,會發出下列exception類型:
allocate_array() noMem
sort_array()     Int
register_data() string
在適當地點安插try blocks及相應的catch子句,用以處理那些exceptions。catch子句只需簡單打印錯誤信息即可。
int *alloc_and_init( string file_name )
{
        ifstream infile( file_name.c_str() );
        int elem_cnt;
        infile >> elem_cnt;
        try
{
       int *pi = allocate_array( elem_cnt );
 
       int elem;
       int index = 0;
       while( cin >> elem )
       {
              pi[ index++ ] = elem;
       }
       sort_array( pi, elem_cnt );
       register_data( pi );
       return pi;
}
catech( const noMem &n )
{
       cout << “allcoate_array() error” << n << endl;
}
catch( int i )
{
       cout << “sort_array() error ” << i << endl;
}
catch( const string &s )
{
       cout << “register_data() error ”<< s << endl;
}
}
19.   int *alloc_and_init( string file_name )
{
        try
        {
               ifstream infile( file_name.c_str() );
               if( !infile )
               {
       throw “cannot opern file”;
}
int elem_cnt;
infile >> elem_cnt;
if( !infile || elem_cnt <= 0 )
{
       throw “invalid elem_cnt”;
}
int *pi = allocate_array( elem_cnt );
 
int elem;
int index = 0;
while( cin >> elem )
{
       if( index >= elem_cnt )
       {
              throw “too many input elements”;
}
pi[ index++ ] = elem;
}
 
sort_array( pi, index );
register_data( pi );
return pi;
}
catch( const noMem &m )
{
       cout << “allocate_array() error ” << n << endl;
       throw;
}
catch( int i )
{
       cout << “sort_array()” << i << endl;
       throw;
}
catch( const string &s )
{
       cout << “register_data() error “ << s << endl;
throw;
}
catch( const char *s)
{
       cout << ‘error: ” << s << endl;
       throw;
}
}
20.   已知namespace:
namespace Exercise
{
        template< class elemType >
        class Array { .. };
 
        template< class Etype >
        void print( Array< Etype > );
 
        class String{ … };
        template< class listType >
        class List { … };
}
 
int main()
{
        cosnt int size = 1024;
        Array< String > as( size );
        List< int > il( size );
 
        Array< String > *pas = new Array< String > (as);
              List< int > *pil = new List< int > ( il );
 
              print( *pas );
}
 
  1. 利用前置修飾詞表示法來訪問Exercise namespace 中類型的定義。
int main()
{
         // 使用修飾詞表示法( ‘qualified name notation’ )
         const int size = 1024;
         Exercise::Array< Exercise::String > as( size );
         Exercise::List< int > il( size );
 
         Exercise::Array< Exercise::String > *pas =
                new Exercise::Array< Exercise::String >( as );
         Exercise::List< int > *pil = new Exercise::List< int >( il );
         Exercise::print( *pas );
}
  1. 利用using declaration來訪問類型定義。
int main()
{
         //使用’using declaration’
        
         using Exercise::String;
         using Exercise::Array;
         using Exercise::print;
         using Exercise::List;
 
         const int size = 1024;
         Array< String > as( size );
         List< int > il( size );
 
         Array< String > *pas = new Array< String >( as );
         List< int > *pil = new List< int > ( il );
         print( *pas );
}
  1. 利用namespace的別名機制(alias mechanism )
int main()
{
         // 使用namespace alias
         namespace E = Exercise;
         const int size = 1024;
         E::Array< E::String > as( size );
         E::List< int > il( size );
 
         E::Array< E::String > *pas = new E::Array< E::String >( as );
         E::List< int > *pil = new E::List< int > ( il );
         E::print( *pas);
}
  1. 利用using directive
int main()
{
         // 使用’using directive’
         using namespace Exercise;
        
         const int size = 1024;
         Array< String > as( size );
         List< int > il( size );
 
         Array< String > *pas = new Array< String > ( as );
         List< int > *pil = new List< int > (il);
         print( *pas );
}
21.   解釋以下vector定義的結果
string pals[] = { “pooh”,”tigger”,’piglet”,”eeyore”,”kanga” };
a.       vector<string> svec1( pals, pals+5); svec1是一個由strings組成的vector并以字符串數組pals作為初值。
b.       vector<int> ivec1( 10 ); ivec1是個vector,擁有10個ints,每一個都初始化為0.
c.       vector<int> ivec2( 10,10); ivec2是個vector,擁有10個ints,每一個都被初始化為10.
d.       vector<string> svec2( svec1 ); svec2是一個vector,并以vector svec1 作為初值。
e.       vector<double> dvec; dvec是個空的vector,內部元素類型為doubles。
22.   已知如下函數聲明,請實現出min()函數,找出并返回vec的最小元素值。使用如下方法:使用for循環,整數索引至vec;使用for循環和一個iterator。
template< class elemType >
elemType min( const vector< elemType > &vec );
 
下面為完整程序:
#include<iostream>
#include<vector>
 
using namespace std;
 
// 其中函数min1()和min2()十分相似,都使用for循环,都在vector成空的时候丢出一// 个exception。
// min1()使用索引法
// min2()使用iterator
// 两者的参数都是一个const vector<elemType> &vec,所以不能使用一般性iterator,而// 必须采用const_iterator;使用解引用(dereference) operator*来取用vector内的数据。
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(cosnt 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()
{
        intarray[] ={ 9, 4, 5, 6, 1, 3, 7, 8, 2, 0 };
        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 2: ” << 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;
}
 
第三章
1.’a’文字常量,表示单一字符a,类型为char。L’a’也表示单一字符a,但是类型为wchar_t,因为前导词L代表宽字符。”a”与L”a”均为字符串,内含单一字符a和一个null字符或是null宽字符。”a”的类型是“常量字符所形成的数组”,L”a”的类型是“常量宽字符所形成的数组”。
    10,10u,10L,10uL,012,0xc全部表示十进制整数常量10.其中10,012和0xA的类型均为int,因为它们没有任何类型的修饰词。012的前导词0表示这是一个八进制常量,0xA的前导词0x表示它是一个十六进制的常量。u->unsigned,L->long,uL->unsigned long。
    没有任何修饰词的3.14类型为double,3.14f表示的是单精度浮点数,3.14L表示的是多精度浮点数。
2.将两个不同类型的字符串连接起来,其行为未有定义->”two”L”some”
 U( unsigned )不能被施加于一个浮点数或文字常量身上->3.45U不合法
    字符串需要在每一行最后加上一个倒斜线才能延续到第二行(或者干脆分为二个字符串):
       eg. “multiple line /
               comment”
 
3.使用cin时不允许同时定义变量-> cin >> int input_value;不合法。
4.说明lvalue和rvalue的差别。。。左值,右值
    所谓lvalue,是变量的地址,或是某个代表对象在内存中位置的表达式。
    所谓rvalue,就是变了的值。
    变量名如果出现在赋值( assignment )运算符的左侧,就是一个lvalue。变量名或者文字常量如果出现在赋值运算符的右侧,他就是一个rvalue。
5.解释以下两组students和name语句的差别:
 a. extern string name;
       string name( “exercise 3.5a” );
b.extern vector<string> students;
vector<string> students;
 第一句中extern string name 为name的声明,告诉编译器name所代表的对象,类型为string;未进行内存分配。
 第二句string name( “exercise 3.5a” )是个定义,告诉编译器name所代表的对象类型string,并进行内存分配,设好初值。
 
 第一句为students的声明,告诉编译器其类型为一个由string组成的vector,没有分配内存。
 第二个句子为一个定义,告诉编译器student所代表的对象类型为string组成的vector,此行将进行内存分配,并以vector的default constructor进行初值设定工资。string的default constructor不会被调用,因为vector为空。
6.变量名的规则
7.下面global object和local object之间的不同
       string global_class;
       int global_int;
 
       int main()
{
       int local_int;
       string local_class;
}
 
两个string对象都将藉由string class的default constructor加以初始化。
global_int会被初始化为0,而local_int不会被初始化(所以其初值可能是任意数值)。
global变量和对象可被其它函数访问;local变量和对象只在它们定义所在的块内可见。
8. 假设有以下定义:
int ival = 1024, ival2 = 2048;
int *pil = &iva1, *pi2 = &ival2, **pi3 = 0;
 
a.                ival = *pi3; 该语句有误,因为它企图将一个int*数值复制给一个int对象,pi3是指针的指针,其值为0,而解引用null指针会造成运行时刻错误。
b.                *pi2 = *pi3; 该语句企图将一个int**赋值给int*
c.                ival = pi2; 企图将一个intx复制给int
d.                pi2 = *pi1; 将int赋值给int*
e.                pi1 = *pi3;pi1被赋值为pi3所指内容,这个操作是合法的。如果pi3并未指向一个有效地址,该行会发生运行时刻错误,但行为不可预期。
f.                 ival= *pi1; pi1未被适当初始化,对一个未被正确初始化的指针进行解引用操作会法师错误。 如果此前的那些定义都成立,那么此行会将ival赋值给ival。这是因为pil被初始化为ival的地址,所以解引用pil会获得ival的值,此值再被赋值给ival。
g.                pil = ival;此行错误,因为pil的类型是int*,而ival的类型是int。
h.                pi3 = &pi2; 此行正确,因为pi3的类型是int**,而pi2的类型是int*,因此&pi2的类型也是int**。
9.        pi = &ival2; pi = pi + 1024; 如果pi为一个int指针,那么在int为32位的环境下就是前进了4096字节。如果pi最初指向一个有着适当大小的数组,那么这就不见得是个错误。
10.    int foobar( int *pi )
{
        *pi = 1024;
        return *pi;
}
 
int main()
{
        int *pi2 = 0;
        int ival = foobar( pi2 );
        return 0;
}
问题在于pi2定义时并未指向一块已获分配的空间,而foobar()却企图对其参数所指的内存做写入操作。除非指针被赋以一个实际值,否则运行时刻的行为没有定义。
int foobar( int *pi )
{
        if( pi )
{
       *pi = 1024;
       return *pi;
}
else
{
       return 0;
}
}
int main()
{
        int ival2 = 0;
        int ival = foobar( &ival2 );
        return 0;
}
11.    指针的使用准则
12.    a.char ch =”lfjalskdjfowjwoei”; ch是单个字符,却被初始化为一个字符串。
c.       int ival = &ch; char *pc = &ival; 企图以不兼容的数值作为变量的初值
d.       string st( &ch ); 语法上正确。但是如果ch未被正确初始化,st可能会有不让预期的结果。另一个重点是,string( cosnt char * ) constructor期望获得一个以null为结束符号的char数组。
e.       没有任何隐式的转换可以将string或const char转换为char *。
f.        没有可以接受的转换行为可以将int *转换为string。
g.       将一个int值赋值给char可能会发生上溢( overflow )
13.    const int *pic; pic是一个指针,指向一个类型为int的常量对象。pic本身可以被修改,但是它所指的对象内容不可被修改-à const ( int * pic)
int *const pic; cpi是一个常量指针,指向一个类型为int的对象,cpi不可修改,但是其所指对象可以修改-à int ( *const cpi )
const int *const pic; cpic是常量指针,指向一个类型为int的常量对象。不论cpic或是其所指对象都不可以修改。
14.    bool is_equal( const int *ia, int ia_size, const vector<int> &ivec)
{
        int i = 0;
for( vector<int>::const_iterator it = ivec.begin(); it != ivec.end(); ++it )
{
       if( i == ia_size )
              break;
       if( ia[i++] != *it )
              return false;
}
return true;
}
15.    inline String String::operator+( const String &rhs ) const
{
        String newString;
 
        if( !rhs._string )
               newString = *this;
        else if( !_string )
               newString = rhs;
        else{
               newString._size = _size + rhs._size;
               newString._string = new char[ newString._size + 1 ];
               strcpy( newString._string, _string );
               strcat( newString._string, rhs._string );
}
return newString;
}
第四章
1. 整数除法和浮点数除法
2.% modulus operator 取余
3. D:/Program Files/Microsoft Visual Studio 9.0/VC/include/limits,climits,cfloat,limits.h
4. c++语言保证,凡是表达式中含有logical AND( && )和logical OR( || )操作符,其计算方法必定由左至右。
5.二元操作符的计算次序在C++ Standard中并没有被明确定义下来,编译器可以自由的提供最佳的实现方式。
6.compound assignment复合赋值操作符(eg. +=,-=,*=, /=,etc.)的意义:
       a op= expression -à a = a op{ expression }è a += a + 1à a = a + ( a + 1);
7.对复数的复合赋值运算:
       #include <complex>
       inline complex<double>& operator +=( complex<double> &cval, double dval )
{
       return cval += complex<double>( dval );
}
8.复数的递增运算(increment)
       前置递增:: 以by-reference方式传递
       #include <complex>
       inline complex<double>& operator++( complex<double> &cval )
       {
              return cval += complex<double>( 1 );
}
后置递增:: 以by-value方式传递,多加一个int参数
inline complex<double> operator++( complex<double> &cval, int )
{
       complex<double> oldcval = cval;
       cval += complex<double>( 1 );
       return oldval;
}
9. bitwise AND (&)à按位与 eg 0101&0111à 0101
10.
inline void bit_turn_on( unsigned int &ui, int pos )
{
       ui |= ( 1 << pos );
}
 
inline void bit_turn_off( unsigned int &ui, int pos )
{
       ui &= ~( 1 << pos );
}
 
inline void flip_bit( unsigned int &ui, int pos )
{
       ui ^= ( 1 << pos );
}
 
inline bool bit_off( unsigned int ui, int pos )
{
       return !bit_turn_on( ui, pos );
}
 
 
int main()
{
       unsinged int ui = 0xd3; // 1101 0011 in binary;
       // bits are numbered from the right, starting at position 0
 
       cout << “ui in hex: ” << hex << ui << ‘/n’;
 
       // turn on the 4th bit from the right
       bit_turn_on( ui, 3 );
       cout << “result should be ‘db’ , it is ” << hex << ui << ‘/n’;
 
       // turn off the 4th bit from the right
       flip_bit( ui, 3 );
       cout << “result should be ‘db’, it is ” << hex << ui << ‘/n’;
 
       // flip the 4th bit from the right
       flip_bit( ui, 3 );
       cout << “result should be ‘d3’, it is ” << hex << ui << ‘/n’;
 
       cout << “4th bit should be 0, it is ” << bit_on( ui, 3 ) << ‘/n’;
 
       cout << “1st bit should be 1, it is ” << bit_on( ui, 0 ) << ‘/n’;
 
       return 0;
}
11.分别利用typedef和function template机制重写bit_on() inline函数
bit_on() 的typedef版本:
 
**在c++程序中调用被c编译器编译后的函数,为什么要加extern”C”?
答:c++语言支持函数重载,c语言不支持函数重载。函数被c++编译后在库中的名字与c语言不同。假设某个函数原型为void foo( int x, int y );
       该函数被c编译器编译后在库中的名字为_foo,而在c++编译器中则会产生像_foo_int_int之类的名字。
       c++提供了c连接交换指定符号extern”C”来解决名字匹配问题。
4.12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
//尽可能的把最长循环放在内层
 
 
!!!!const数据成员只能在类的初始化列表中进行初始化,因为const数据成员只在某个对象生存期内为常量,而对于不同的类而言是可变的。
so:
class A
{
       A(int i);
       const int m_const;
};
 
A::A(int i): m_const(i)
{
// empty
}
!!函数的参数与返回值的传递方式:按值传递(pass by value),按指针传递(pass by pointer),按引用传递(pass by reference)。函数没有参数的,要用void填充。
 
source,destination::void StringCopy(char *strDestination, char *strSource)
àStringCopy(strDestination,strSource/”hello world”);
常量指针const TYPE *;常量引用const TYPE &;
 
!!有时候函数原本不需要返回值,但是为了增加灵活性如支持链式表达,可以附加返回值:
       char *strcpy(char *strDest, const char *strSrc);
!!@@对于赋值函数,应当用“引用传递”&的方式返回值。如果用值传递方式,虽然功能正确,但由于return语句要把*this拷贝到保存返回值的外部存储单元中,增加了不必要的开销,降低了赋值函数的效率。
!return不可返回指向stack memory的指针或者引用,因为该内存在函数体结束时被自动销毁。
 
!!!!断言assert是仅在debug版本起作用的宏,它用于检查不应该发生的情况。是宏,不是函数!!
 
!!!引用的规则:
1.       引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)
2.       不能有null引用,引用必须与合法的存储单元关联。(指针可以是null)
3.       一旦引用被初始化,就是不能改变引用的关系。
int i = 5;
int j = 6;
int &k = i;
                            k = j; //把j的值赋值给k引用的对象,即k,i均变成6.
 
 
内存管理部分:
1.       使用内存前检查指针是否为null。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果用malloc或new来申请内存,应该用if(p==NULL)或if(p!=NULL)来进行防错处理。
2.       内存申请后要初始化。。。。。
3.       操作中超出内存边界。
4.       释放内存。。。malloc->free;new->delete;;释放后将指针设置为null
5.       释放了内存却继续使用它。。。。。。。
6.       申请内存后立即检查指针是否为null
7.       c++无法知道指针所指向内存的容量,除非在申请内存时记住它。
8.       sizeof(pointer)得到的是一个指针变量的字节数à sizeof(char*)
9.       如果需要用指针参数去申请内存,那么应该改用“指向指针的指针:”
void GetMemory(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
}
 
void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100); // it,s &str here, not just str….
 strcpy(str, “hello”);
 cout << str << endl;
 free(str);
}
                     B.利用函数返回值来传递动态内存:
                            char *GetMemory(int num)          //函数返回值类型为char *
                            {
                                   char *p = (char *)malloc(sizeof(char) * num);
                                   return p;
}
 
void Test(void)
{
       char *str = NULL;
       str = GetMemory(20);
       strcpy(str,”hello”);
       cout << str <<endl;
 
       free(str);
}
!!!!!!malloc/free为库函数,new/delete为运算符!new/delete 完全覆盖了malloc/free的功能。
 
内存耗尽的处理:
1.       判断指针是否为NULL,如果是则马上用return语句终止本函数。
2.       判断指针是否为NULL,如果是则马上用exit(1)终止整个程序。
3.       为new和malloc设置异常处理函数。
 
malloc的返回值是void *,所以调用malloc时要显示的进行类型转换:
       int *p = (int *)malloc(sizeof(int) * length); // 后面的*为乘号,用于计算要分配的内存数
 
 
new内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而已,new在创建动态对象的同时完成了初始化工作。delete释放数组时需要delete [] arrayName;
 
 
在c++中只能靠参数而不能靠返回值类型的不同来区分重载函数。globalFunction和memberFunction重名不算重载,因为其作用域不同。全局函数被调用时要加::,比如::Point(abc)
 
 
重载:
1.       相同的范围à同一个类中
2.       函数名字相同
3.       参数不同
4.       virtual关键字可有可无
覆盖:
1.       不同的范围à分别位于派生类和基类
2.       函数名字相同
3.       参数相同
4.       基类函数必须有virtual关键字
 
通过重载来调用base class中的函数,否则BaseClass::Function会被隐藏。
void Derived::f(int x)
{
       Base::f(x);
}
 
 
!!!如果函数有多个参数,参数只能从后向前挨个缺省
eg. void Foo(int x, int y = 0, int z = 0);       // RIGHT
eg. void Foo(int x = 0, int y, int z = 0);       // WRONG
 
c++中使用关键字operator加上运算符来表示函数,叫做运算符重载
eg. Complex operator +(const Complex &a, const Complex &b);
运算符与普通函数在调用时的不同之处在于:对于普通函数,参数出现在圆括号内;而对于运算符,参数出现在其左、右侧。
如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符;如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。
 
定义在类声明之中的成员函数将自动成为内联函数:
eg. class A
       {
              public:
                     void Foo(int x, int y) { …….. }          // 自动地成为内联函数
}
 
类的四个缺省函数:
       A(void); //缺省的无参数构造函数
       A(const A &a);       //缺省的拷贝构造函数
       ~A(void);              //缺省的析构函数
A& operator =(const A &a); //缺省的赋值函数
 
类的const常量只能在初始化列表里被初始化。
 
String::String(const char *str)
{
       if(str==NULL)
       {
              m_data = new char[1];
              *m_data = ‘/0’;
}
else
{
       int length = strlen(str);
       m_data = new char[length+1];
       strcpy(m_data, str);
}
}
 
String::~String(void)
{
       delete [] m_data;
       // 由于m_data是内部数据类型,也可以写成delete m_data;
}
 
String c = a;   //调用了拷贝构造函数,不如String c(a);
 
String::String(const String &other)
{
       // 允许操作other的私有成员m_data
       int length = strlen(other.m_data);
       m_data = new char[length+1];
       strcpy(m_data, other.m_data);
}
 
String & String::operate = (const String &other)
{
       // 1.检查自赋值!
       if(this == &other) //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this == &other,地址间是否相同
              return *this;
 
       // 2.释放原有的内存资源
       delete [] m_data;
 
       // 3.分配新的内存资源,并复制内容
       int length = strlen(other.m_data);
       m_data = new char[length+1];
       strcpy(m_data, other.m_data);
 
       // 4.返回本对象的引用,实现链式表达,如a=b=c
       return *this;
}
 
避免使用拷贝构造函数和赋值函数。。将两个函数声明为私有函数
 
基类的构造函数、析构函数、赋值函数都不能被派生类继承
1.       派生类的构造函数应在其初始化表里调用基类的构造函数。
2.       基类与派生类的析构函数应该为虚函数à virtual!!!!!!!!!!!!!
3.       在编写派生类的赋值函数时,注意不要忘记对基类的数据成员重新赋值。!!
class Base
{
public:
        …
        Base & operate =(const Base &other); // 类Base的赋值函数
private:
        int m_i, m_j, m_k;
};
 
class Derived : public Base
{
public:
        Derived & operate =(const Derived &other);
private:
        int m_x, m_y, m_z;
};
 
Derived & Derived::operate =(const Derived &other)
{
        //1.检查自赋值
        if(this == &other)
               return *this;
       
        //2.对基类的数据成员重新赋值
        Base::operate =(other);   //因为不能直接操作私有数据成员
 
        //3.对派生类的数据成员赋值
        m_x = other.m_x;
        m_y = other.m_y;
        m_z = other.m_z;
 
        //4.返回本对象的引用
        return *this;
}
 
对象object,类class,实例instance;继承inheritance,组合coposition
COM,CORBA
 
const:
1.       const只能修饰输入参数,不能修饰输出参数。
2.       如果参数是按指针传递,那么const修饰可以防止意外的改动指针
3.       如果参数采用按值传递,由于函数将自动产生临时变量用于复制该参数,该输入参数就无需保护,不需要const
4.       用常理引用可以提高效率,不需要产生临时对象
5.       给以按指针传递方式的函数返回值加const修饰,那么函数返回值将不能被修改,该返回值只能被赋给const修饰的同类型指针
6.       const函数不会修改变量à int GetCount(void) const; //const成员函数
 
 
C++代码审查表:
1.       文件结构:
a.       头文件和定义文件的名称是否合理?
b.       头文件和定义文件的目录结构是否合理?
c.       版权和版本声明是否完整?
d.       头文件是否使用了ifndef,define,endif
e.       头文件中是否分类好了声明和定义
2.       程序的版式
a.       空行是否得体
b.       代码行内的空格是否得体
c.       长行拆分是否得体
d.       {}是否各占一行并且对齐于同一列
e.       一行代码是否只做一件事
f.        if,for,while,do等语句独占一行,不论执行语句多少都加{}
g.       在定义变量或参数时,是否将修饰符*和&紧靠变量名
h.       注释是否清晰并且必要
i.        注释是否有错误或者可能导致误解
j.        类结构的public,protected,private顺序是否在所以的程序中保持一致
3.       命名规则
a.       命名规则是否与所采用的操作系统或开发工具的风格保持一致
b.       标识符是否直观可读
c.       标识符长度是否符合min-length&&max-information
d.       程序中是否出现相同的局部变量和全局变量
e.       类名、函数名、变量、参数、常量的书写格式是否遵循一定规则
f.        静态变量、全局变量、类的成员变量是否加了前缀
4.       表达式与基本语句
a.       是否用括号清楚的确定复杂表达式的操作顺序
b.       是否编写太复杂或者多用途的复合表达式
c.       是否将复合表达式与真正的数学表达式混淆
d.       是否用隐含错误的方式写if语句
                        i.              将bool变量直接与true、false或者1、0比较
                      ii.              将浮点变量用==或!=与任何数字比较
                    iii.              将指针变量用==或!=与NULL比较
e.       如果循环体内存在逻辑判断,并且循环次数很大,是否已经将逻辑判断移到循环体外
f.        case语句结尾是否忘了加break
g.       是否忘记写switch的default分支
h.       使用goto时是否留下隐患
5.       常量
a.       是否使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串
b.       在c++程序中是否使用const常量取代宏常量
c.       如果某一常量和其它常量关系密切,是否在定义中包含了这种关系
d.       是否误解了类中的const数据成员?因为const数据成员只在某个对象生存期内为常量,而对于整个类而言可变
6.       函数设计
a.       参数的书写是否完整?
b.       参数命名、顺序是否合理
c.       参数个数是否太多
d.       是否使用类型和数码不确定的参数
e.       是否省略了函数返回值的类型
f.        函数名字与返回值类型在语义上是否冲突
g.       是否将正常值和错误标志混在一起返回?正常值应当使用输出参数获得,而错误标志用return语句返回
h.       在函数体的入口处,是否用assert对参数的有效性进行检查
i.        滥用了assert?例如混淆非法情况与错误情况,后者是必然存在并且一定要处理的
j.        return语句是否返回指向栈内存的指针或引用?
k.       是否使用const提高函数的健壮性?const可以强制保护函数的参数,返回值,甚至函数的定义体
7.       内存管理
a.       用malloc或new申请内存后,是否立即检查指针值是否为NULL?
b.       是否忘记数组和动态内存赋初值
c.       数组或指针的下标是否越界
d.       动态内存的申请与释放是否配对
e.       是否有效的处理了内存耗尽问题
f.        是否修改指向常量的指针的内容
g.       是否出现野指针
                        i.              指针变量未初始化
                      ii.              用free或delete释放内存后忘记将指针设置为NULL
h.       是否将malloc/free和new/delete混淆使用
i.        malloc语句是否正确无误?字节数是否正确,类型转换是否正确
j.        在创建与释放动态对象数组时,new/delete的语句是否正确无误
8.       c++函数的高级特性
a.       重载函数是否有二义性
b.       是否混淆了成员函数的重载、覆盖与隐藏
c.       运算符的重载是否符合制定的编程规范
d.       是否滥用内联函数,例如函数体内的代码比较长,函数体内出现循环
e.       是否用内联函数取代了宏代码
9.       类的构造函数、析构函数和赋值函数
a.       是否违背了编程规范而让c++编译器自动为类产生四个缺省函数
                        i.              缺省的无参数构造函数
                      ii.              缺省的拷贝构造函数
                    iii.              缺省的析构函数
                     iv.              缺省的赋值函数
b.       构造函数中是否遗漏了某些初始化工作
c.       是否正确的使用构造函数的初始化表
d.       析构函数是否遗漏了某些清除工作
e.       是否错写、错用了拷贝构造函数和赋值函数
f.        赋值函数一般分四个步骤:
                        i.              检查自赋值
                      ii.              释放原有内存资源
                    iii.              分配新的内存资源并复制内容
                     iv.              返回*this
g.       是否正确的编写了派生类的构造函数、析构函数、赋值函数
                        i.              派生类不可能继承基类的构造函数、析构函数、赋值函数
                      ii.              派生类的构造函数应在其初始化表里调用基类的构造函数
                    iii.              基类与派生类的析构函数应该为虚à virtual
                     iv.              在编写派生类的赋值函数时,应注意不要忘记对基类的数据成员重新赋值
10.   类的高级特性
a.       是否违背了继承和组合的规则
                        i.              若在逻辑上B是A的一种,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性
                      ii.              若在逻辑上A是B的一部分,则不允许B从A派生,而要是用A和其它东西组合出B
11.   其它问题
a.       数据类型问题
                        i.              变量的数据类型有错误?
                      ii.              存在不同数据类型的赋值?
                    iii.              存在不同数据类型的比较?
b.       变量值问题
                        i.              变量的初始化或缺省值有错误?
                      ii.              变量发生上溢或下溢?
                    iii.              变量精度够嘛?
c.       逻辑判断问题
                        i.              由于精度原因导致比较无效?
                      ii.              表达式中的优先级有误?
                    iii.              逻辑判断结果颠倒?
d.       循环问题?
                        i.              循环终止条件不正确?
                      ii.              无法正常终止?(死循环)
                    iii.              错误的修改循环变量
                     iv.              存在误差基类?
e.       错误处理
                        i.              忘记进行错误处理
                      ii.              错误处理程序块一直没有机会被运行?
                    iii.              错误处理程序块本身就有毛病?如报告的错误与实际错误不一致,处理方式不正确等等
                     iv.              错误处理程序块是马后炮?如在被它调用之前软件已经出错
f.        文件I/O问题
                        i.              对不存在的或者错误的文件进行操作?
                      ii.              文件以不正确的方式打开?
                    iii.              文件结束判断不正确?
                     iv.              没有正确关闭文件?
 
 
operator int() const
{
       return denom == 1 ? numer : int(longDemial());
}
 
const Rational & Rational::operator=(const Rational &rhs)
{
       if(this!=&rhs)
       {
              numer = rhs.numer;
              denom = rhs.denom;
}
return *this;
}
 
const Rational & Rational::operator+=(const Rational &rhs)
{
       numer = numer * rhs.denom + rhs.numer * denom;
       denom = denom * rhs.denom;
 
       reduce();
 
       return *this;
}
 
Rational Rational::operator*(const Ratioal & rhs) const
{
       Rational answer(*this);
       answer += rhs;
       return answer;
}
 
const Rational & Rational::operator+() // Prefix form
{
       numer += denom;
       return *this;
}
 
Rational Rational::operator++(int) // Postfix form
{
       Rational tmp = *this;
       numer += denom;
 
       return tmp;
}
 
bool Rational::operator!() const
{
       return !numer;
}
 
const Rational & Rational::operator+() const
{
       return *this;
}
 
Rational Rational::operator-() const
{
       return Rational(-numer, denom);
}
 
ostream & operator << (ostream &out, const Rational & rhs);
istream & operator >> (istream &in, Rational &rhs);
 
istream & operator>>(istream &in, Rational &value)
{
       in >> value.numer;
       value.denom = 1;
      
       char ch;
       in.get(ch);
 
if(!in.eof())
{
       if(ch == ‘/’)
{
       in >> value.denom;
       value.fixSigns();
       value.reduce();
}
else
{
       in.putback(ch);       // unread ch
}
}
return in;
}
 
 
class auto_ptr:
1.std::auto_ptr<ClassA> ptr(new ClassA);           // RIGHT
std::auto_ptr<ClassA> ptr2 = new ClassA;       // ERROR
auto_ptr会删除其所有物件,所以这些物件绝对不能同时被其它物件拥有!!
 
std::auto_ptr<ClassA> ptr;
ptr = std::auto_ptr<ClassA>(new ClassA)
 
@@@@@@@@@@@@@@@@@@@@@@69/424
 
 
 
X x;
Y y(x);           显式转换,根据型别x产生量一个型别y的新物件;
 
X x;
Y y = x;         隐式转换,产生量一个型别y的新物件;
 
根据c++标准规则,只有两种main()是可以移植的:
int main()
{
       …
}
 
int main(int argc, char* argv[])
{
       ..
}
原创粉丝点击