C++课程笔记(3)——Part2 Abstraction Mechanisms

来源:互联网 发布:欧阳娜娜 知乎 编辑:程序博客网 时间:2024/06/14 01:49

Reference Book: 《C++ Primer》5th Edition

--------------------------------------------------------------------------------------------------------------------------------------

1. Classes and Objects

(1)Concepts and Classes

ADT(Abstract Data Type):should not mention any implementation details at all. OnlyDomain andOperations of a type are described.


(2)Member Functions P230 P232

①member functions and data members

 struct Date {    int d, m, y ;    void init_date(int, int, int);   void add_year(int);   void add_month(int);   void add_day(int); };// There is an encapsulation between // the data and the operations.
Functions declared within a class (or struct) definition are called member functions (or function members).成员函数
Data within a class (or struct) are called data members (or member data). 数据成员
Member function called in the form:
       object.f(…);
 or
       object_ptr->f(…);

②Qualifying Member Functions on Definition 

void Date::init_date(int dd,                int mm, int yy) {   d=dd;   m=mm;   y=yy; }
③Using Members within Member Function's Definition

Within member function, member names can be used without explicit reference to an object. 

Names refer to that member of the object for which the function was called.

(3)Access Control
Access Control Specifier P240-241

public members are the user interface of the type.
private members are internal representation of the type, user should not understand or refer to them directly. 
The member functions can access all members of the class. 


(4)Constructors P235-239 P259-263


The use of general functions to initialize class objects is inelegant and error-prone.
       -- Forgetting to initialize 
        -- Being initialized twice
The use of special purpose functions to do initialization – constructors – is better.

Constructors have the same name as the class, and no type specified.
When a class has a constructor, all objects of that class will be initialized by a call to the constructor.
Object initialization statements should provide the arguments that the constructors need.

 class Date { … public:   Date(int,int,int);}; Date today=Date(27,03,2017); Date MayDay (01,05,2017); Date sb_birthday;   // ERROR Date nationalDay(01,10);// ERROR
Initialization Notations

T o = T(arglist);   {arglist}T o(arglist);   {arglist}  // abbreviated form T o=arg;   // when the number of arguments is 1T o;           // when the number of arguments is 0T o( ); // different meaning!!   T o{}; // OK
③Overloading Constructors
④Constructors with Default Arguments P260

class Date { …  Date(int dd=0,int mm=0,int yy=0); }; Date today(/*getting args from system*/);  Date::Date(int dd,int mm,int yy) { d=dd?dd:today.d;   m=mm?mm:today.m;   y=yy?yy:today.y;   // check y,m,d is valid }
⑤Object Size
Size of an object is greater than or equal to the sum of sizes of all non-static data members (excluding member functions) of a class.
Example:
     Date day;   sizeof(day) >=  sizeof(d)+sizeof(m)+sizeof(y)


(5)Static Members P268-271

A variable that is part of a class, yet is not part of an object of that class, is called a static data member. 
A function that needs access to members of a class, yet does not need to be called for a particular object, is called a static member function.
Static members are used to replace global data or functions that are specific to a class.

static members (functions and data) must be defined somewhere.

Notation 1 :
                     classname::member
Notation 2 :
                     object.member  
                     obj_ptr->member


   Notation 1  is better.


(6)Default Copy P239-240
By default, objects can be copied                     – both on initialization and assignment.
The semantics of default copy is memberwise copy (bitwise copy)按位拷贝
The default copy may be incorrect
    Defining copy semantics may be necessary. 

 Date d{today}; //initialization void f(Date& d) {    d = today;   //assignment }

(7)Constant Member Functions P231-232

Keyword const is a part of the function type, you must specify const on all declarations and its definition.

 class Date {  …    int d; public:    int get_day() const;  … }; int Date::get_day() const { return d++;} // ERROR int Date::get_day()  { return d; } 
A constant member function can be called for both constant and non-constant objects, but a non-constant member functions can be called for non-constant objects only. That is,constant member functions cannot call non-constant member functions.常量对象不能调用非常量成员函数。


(8) this pointer P231 P233 P246-248


 In non-static member function, the keyword this is a pointer to the object for which the function is called.静态成员函数中不能使用this指针。
For non-constant member function, this is of the type T* const;
For constant member function, this is of the type const T* const;
 Most uses of this are implicit, but explicit uses are possible. 大部分为隐式调用,但也可以显示调用this指针。

②Cascade Function Calls P233

It is useful to return a reference to an object so that the operations can be chained.使链式操作变为可能。
Example:
     cout << a << b; //operator<< is a function indeed
     cout << a    returns cout, 
     so following call to 
           cout << b      is possible.

class Date{Date& add_year(int); Date& add_month(int); Date& add_day(int); };void f(Date& d){ d.add_day(1).add_month(1).add_year(1);} Date& Date::add_year(int n) {    …    y += n;    return *this; }

(9)Structures and Classes
(10)In-Class Function Definitions
(11)Concrete Classes 可继承实体类
Concrete classes are those frequently used, simple, self-contained classes.They are not used in the class hierarchies.Concrete classes are a foundation of elegant programming.Date is an example of concrete class. 
①Typical Concrete Classes:
Constructors"get" functions             -- constant read functions"set" functions"copy" functionsInner exception classes
②Date Class示例:

 // class specification   Date.h class Date { public:  // user interface   enum Month {jan=1,feb,…,dec};  class Bad_date {}; //exception class  Date(int=0;int=Month(0);int=0);  int day() const;  int month() const;  int year() const;  string string_rep() const;  void char_rep(char*) const;  static void set_default(int,Month,int);  Date& add_year(int);  Date& add_month(int);  Date& add_day(int);private://internal representation  int d,m,y;  static Date default_date;// 静态成员可以使用不完全类型 }; Date Date::default_date(01,01,2017);      // class implemetation   Date.cpp // every member function should be defined Date::Date(int dd,Month mm,int yy){ if (yy==0) yy=default_date.y;  if (mm==0) mm=default_date.m;  if (yy==0) yy=default_date.d;  int max;  switch(mm) {    case feb: max=28+leapyear(yy); break;    case apr:case jun:case sep:case:nov:              max=30;break;            case jan:case mar:case may:case:jul:    case aug:case oct:case dec:              max=31;break;    default: throw Bad_date();  }  if (dd<1||max<dd) throw Bad_date();  y=yy;  m=mm;  d=dd; }
③Separation of Specification and Implementation
Specification       *.h
Implementation  *.cpp
For user : *.h  *.obj
If specification does not change, user program needn't change anyway when implementation changes 
(use another data representation or use other algorithms to define member functions).

(12)Helper Functions P234-235
A class has a number of functions associated with it that needn't be defined in the class itself because they don't need direct access to the data representation.
Example:
      int diff(Date,Date);//date interval
     bool leapyear(int);
     Date next_weekday(Date);


对于helper function的声明方法:
   1. Putting helper functions' declaration 
        into Date.h
or
   2. Enclosing the class and its helper 
        functions in a namespace.


(13)Overloaded Operators
  inline bool operator==(Date a,Date b)
  { return a.day()==b.day() &&
           a.month()==b.month() &&
           a.year()==b.year() ;
  }

(14)Destructors P444-446
Contrary to constructors, destructors clean up and release resources.
They are called implicitly just before an object is destroyed.
For example :
    Constructors allocate free store memory or open files, destructors recycle memory or close files. 
Destructor name is ~constructor, and it cannot be specified a return type or parameters.


(15)Default Construction P236 P262-263
Default constructor is a constructor that can be called without supplying an argument.
Default constructor can be defined by the programmer.
If there is no constructor defined by the programmer, system provides a default constructor for the class (do nothing).


(16)Local Objects
A local object is created when the program control passes through the location of the local object definition statement, 
and is destroyed when the program control leaves the block in which the local object is defined.
Destruction order of local objects are in reverse order of construction.

  void f(int i) { Table a;   Table b;   if (i>0)    {  Table c; … }   Table d; }  //construction order: a b (c) d    //destruction order : (c) d b a   

(17)User-defined Copy P440-444
Using default copy for class with pointer members may cause an undesired result. 
The copy semantics must be defined by  the programmer.

 class Table { … //copy constructor Table(const Table&);        // for initialization //assignment operator Table& operator=(const Table&);        // for assignment };Table::Table(const Table& t){  p=new Name[sz=t.sz]; uninitialized_copy_n(t.p,sz,p);} Table& Table::operator=(const Table& t) { if (this!=&t) // avoid self-assignment 调用this指针防止自己赋值给自己   {  delete[] p;      p=new Name[sz=t.sz];      uninitialized_copy_n(t.p,sz,p);   }   return *this; }


(18)Dynamic Objects P407-411

An object created on the free store has its constructor called by the new operator and exists until thedelete operator is applied to a pointer that points to it.


(19)Member Objects P258
A. Member Objects: // 成员是某一类类型

In some classes, their data members are of the type of another class.Member objects must be constructed before constructor of the class is executed.Arguments of constructor for member object must be provided by the constructor of the class in the initializer list.

 class Club {  string name;  Table members;  Table officers;  Date founded;  …  Club(const string& n , Date fd); };Club::Club(const string& n , Date fd )   :name(n),members(),officers(),founded(fd){   …}/* the constructors are called in the order in which the members are declared in the class rather than the order in which the members appear in the initializer list. The member destructors are called in the reverse order. */If a member constructor needs no argument, the member need not be mentioned in the member initializer list.  Club::Club(const string& n , Date fd )   :name(n),founded(fd) {   … }

B. Constant and Reference Members:// 成员是const或者引用时
Like member objects, constant and reference members must be initialized using an initializer rather than assignment in constructor definition.
Other members may use assignment or initializer.

 class X { const int a, Club b; int& c; int d; … }; X::X(int i1,const string& n1,       Date d1,int i2,int i3 )    :a{i1},b{n1,d1},c{i2},d{i3} { }


(20)Arrays of Objects
Constructing element-by-element.
If an array declaration has no initializer for every element, the default constructor must have existed.

  Table a1[3]={10,20,30}; Table a2[100]; class X { … X(int);};  // there is no default constructor X a3[10]; //error


(21)Local Static Objects
The constructor for a local static object is called the first time the program control passes through the object's definition.
The destructors for local static objects are called in the reverse order of their constructions when the program terminates.

  void f(int i) { static Table tbl1;   …   if (i)   {  static  Table tbl2; … } } int main() { f(0); f(1); f(2); }/* construction tbl1,tbl2 only once,   destruction when main ends in the    order tbl2,tbl1 */

(22)Nonlocal Objects
A variable defined outside any function ( global, namespace, class static) is constructed before main and is destroyed after main.
Constructors for nonlocal objects in a compilation unit are executed in the order their definitions occur.
No guarantees are made about the order of construction of nonlocal objects in different compilation units.

  class X {   static Table memtbl; }; Table tbl1; Table X::memtbl(20); namespace Z {  Table tbl2; } //construction order:tbl1,memtbl,tbl2//destruction order :tbl2,memtbl,tbl1

(23)Temporary Objects
Temporary objects are generated when the system performs some operations.
Example:
      int a = x*y+z;
            x*y stores value in a temporary object
    and x*y+z stores value in another temporary object before it is assigned to a.
Unless bound to a reference or used to initialize a named object, a temporary object is destroyed at the end of the full expression in which it was created.
A full expression is an expression that is not a subexpression of some other expression.// 若未通过引用绑定或初始化对象,临时对象在表达式结尾会被销毁
Temporary objects may cause some problems. 
Temporary objects may be optimized away by the compiler.

 void f(string& s1,String& s2,string& s3) {    const char* cs=(s1+s2).c_str();   cout << cs;   if(strlen(cs=(s2+s3).c_str())<8 &&                             cs[0]=='a')      // cs used here } void f(string& s1,string& s2,string& s3) {    cout << s1+s2;   string s = s2+s3;   if(s.length()<8 && s[0]=='a')      // s used here }

A temporary object can be used as an initializer for a const reference or a named object.  P471// 临时对象可以初始化const左值引用,右值引用,命名对象(?)
The temporary object is destroyed when its reference or named object goes out of scope.
A temporary object cannot be bound to a non-const reference.



(24)Placement of Objects
new operation creates its object on the free store by default.
We can place objects anywhere by providing an allocator function with extra arguments (first argument is size_t to express size) and then supplying such extra arguments when using new (called placement syntax). // 创建空间在指定的地址

  void* operator new(size_t,void* p) { …  return p; } void* buf =      reinterpret_cast<void*>(0xF00F); X* p2=new(buf)X; //construct an object of X at 'buf'  //calls: operator new(sizeof(X),buf)


2. Operator Overloading
(1)Algebraic Notation
Algebraic notation is a more conventional, concise and convenient notation than functional notation.
The semantics of algebraic notation and those of functional notation are identical, a use of the operator is only a shorthand for an explicit call of operator function.

 class complex {   double re,im; public:   complex(double r,double i)   :re{r},im{i} { }   complex operator+(complex);      complex operator*(complex);};  complex a(1,3.1),b(1.2,2); complex c=b; a=b+c;    b=b+c*a; c=a*b+complex(1,2);a.operator=(b.operator+(c))b.operator=(b.operator+(c.operator*(a)))a*b produces a temporary object



(2)Operator Functions P490

It is impossible to define a new operator token.不能自己定义新的操作符。
The name of an operator function is operator@. (if @  is a word, insert a blank).   
An operator can be declared only for the syntax defined for it in the C++ grammar只可改变操作符语义,不能改变其结合性和优先级。
    -- the arity (Number of operands), associativity and precedence of an operator cannot be changed.
 

(3)Binary and Unary OperatorsP491-493

A. 二元运算符:

A binary operator can be defined by either a non-static member function taking one parameter (and another operand is object itself ) 

or a nonmember function taking two parameters.既可定义为成员函数,也可定义为非成员函数。
For operator@ : aa @ bb
   means:  aa.operator@(bb)
            or operator@(aa,bb)
=, [], () ,-> operators must be defined as member functions.

class X{ public:   void operator+(int);   X(int); }; void operator+(X,X); void operator+(X,double); void f(X a) {  a+1;   // a.operator+(1) 调用类成员函数    1+a;   // operator+(X(1),a)隐式转换,调用非成员函数    a+1.0; // operator+(a,1.0) }

B. 一元运算符:

A unary operator can be defined by either a non-static member function taking no parameter 

or a nonmember function taking one parameters.
For operator@ :  @ aa
   means:  aa.operator@( )
            or operator@(aa)

[discuss suffix ++ and -- operators later] ++和--运算符单独讨论



(4)Predefined Meanings for Operators

Only assignment(=), address-of(&) and comma(,) operators have predefined meanings, others not.
They can be made inaccessible to general users ( use =delete notation) or be given new meanings by your own definitions./// 通过定义为删除或者重新定义


(5)User-defined Meanings for Operators

It is impossible to change operator meanings of built-in types. /// 内置类型的运算符的含义无法改变
A user-defined operator function must be have relation with a user-defined type.
    -- An operator function must either be a member or taking at least one argument of a user-defined type. (operator new and delete are exceptions)
    -- User type argument can be a reference, only pointer to user-defined type is incorrect./// 参数需要用引用类型

An operator function intended to accept a built-in type as its first operand cannot be a member function.  /// 内置类型用作第一个操作数时,操作符不能声明为成员函数 P493
An example: 
                       complex a;
                     2+a →  2.operator(A) 
                     is not right.


(6)Operators and Namespaces

For X x , Y y,  x@y resolution as following:
[1] if X is a class, look for operator@ as a member of X. 
[2] look for operator@ in the context surrounding  x@y.
[3] if X is defined in namespace N, look for operator@ in N.
[4] if Y is defined in namespace M, look for operator@ in M.


      [1]+[2]+[3]+[4] overloading resolution

 namespace std {  class ostream {    ostream& operator<<(const char*);  };  extern ostream cout;  class string { … };  ostream& operator<<(ostream&,                      const string&);  };} int f() { char* p{"Hello"};   std::string s{"world"};   std::cout<<p<<s; } std::cout << p; /* [1],[3] */ std::cout << s; /* [1],[3],[4] */


For unary operator, resolution rule is identical.
No preference is given to members over nonmembers.


(7)An Example -- complex class


(8)Member Operators and Nonmember Operators P493

Rule of thumb:
     --  if operator function inherently modify the 
          value of their first operand, use member.
     --  otherwise, use nonmember. 



(9)Mixed-mode Arithmetic

Combine complex and double for operations.
Example:
        complex + complex
        complex + double
        double    + complex 

        complex += complex
        complex += double

 class complex {   double re,im; public:   complex& operator+=(complex);   complex& operator+=(double); }; complex operator+(complex,complex); complex operator+(complex,double); complex operator+(double,complex); void f(complex x,complex y){  complex r1=x+y;     //operator+(complex,complex)  complex r2=x+1;    //operator+(complex,double)  complex r3=1+y;    //operator+(double,complex)}



(10)Initialization

class complex {  double re,im; public:  complex(double r=0,double i=0)  :re{r},im{i} {  } }; complex a; complex b=1; complex c(1,2);



(11)Copying

(12)Conversions P263-267

A constructor can be regarded as a user-defined conversion when it has only one parameter.// 只接受一个实参的构造函数,提供了类型隐式转换的机制
A user-defined conversion is implicitly applied only if it is unique.
A temporary object is introduced when a conversion takes place.

No implicit user-defined conversions are applied to the left side of operator . or -> . 

 class complex {   double re,im; public:   complex(double r=0,double i=0)   :re{r},im{i} { }               // double  complex   complex& operator+=(complex);}; complex operator+(complex,complex); void f(complex x,complex y)// 通过隐式转化的机制,可以省去(9)中的一些operator+的重载定义{  complex r1=x+y;     //operator+(complex,complex)  complex r2=x+1;    //operator+(complex,complex(double))  complex r3=1+y;    //operator+(complex(double),complex)}
 void f(complex z) {    3+z;   // 3  complex    3.operator+=(z); // ERROR operator.的左侧3无法进行隐式转换    3+=z;            // ERROR 3转化为一个complex类型的临时对象,不能作为左值 }


(13)Literals /// 定义complex类字面值的方法

It is impossible to define literals like 1.2 or 100 as literals of type double or int.
complex(1.1,2.2) can be regarded as a complex literal. 

User-defined Literal:  operator"" 
    parameter: long double / const char*
Ex:
  complex operator"" i(long double d)
  { return {0,d}; }

  auto a=2i; /// 参数用long double或者常量字符数组来表示;Complex类型的常数,标志符为i;a即complex类型的常数;



(14)Additional Functions

(15)Conversion Operators

/// 定义运算符完成类型转换

A. Drawbacks of constructor as conversion:
- An implicit conversion from a user-defined type to a built-in type cannot be specified.// 无法执行自定义类到内置类型的隐式转换
- A conversion from a new class to previously defined class without modifying the old class cannot be specified. 

B. Solution: Defining conversion operator
 X::operator T( )      XT
Member function without type specification.
Conversion operator should not lost information in the conversion process.

/// 不写返回类型(实际返回T类型,以此表明这是一个特殊的函数),但有返回值;类型转换运算符必须是一个成员函数,名称必须是operator T(要转换为的类型名,如                double);类型转换运算符尽可能不丢失信息,实际上complex→double,double→complex虚部的信息丢失;

 class Tiny {  // [0,63]  char v;    void assign(int i)// i(int类型)赋值给v之前需要通过assign()进行范围检查;  { if (i&~077) throw Bad_range();       v=i; } public:  class Bad_range {};  Tiny(int i=0){assign(i);}  Tiny& operator=(int i)    {assign(i);return *this;}  operator int() const {return v;}}; 
/// 类型转换举例  Tiny c1=2;     // int→Tiny  Tiny c2=62;    // intTiny  Tiny c3=c2-c1; // 2 Tiny→int,int→Tiny Tiny c4=c3; int i=c1+c2; // 2 Tiny→int c1=c1+c2;// 2 Tiny→int 64 Bad_range i=c3-64; // Tiny→int c2=c3-64; // Tiny→int –4 Bad_range c3=c4;

C. 表达式用作条件时,无论是否声明为explicit,都发生隐式转换

 while (cin>>x) …


   cin>>x returns cin, which is of type istream, cin is implicitly convert to a value to indicating the state of cin. 

This is completed by the conversion operator defined in class istream. 


(16)Ambiguity Resolution P517-521

A.

If both user-defined conversion and user-defined operators are defined, it is possible to get ambiguities.
It is best to rely on user-defined conversions or user-defined operators for a given type, but not both.

 int operator+(Tiny,Tiny); void f(Tiny t,int i) {    t+i; }/* operator+(int,int)          // tint   operator+(Tiny,Tiny)    // iTiny*/


B. 用户定义的类类型转换 P263-266
An assignment of a value of type V to an object of class X is legal if there is an assignment operator X::operator=(Z) so that V is Z or there is a unique conversion of V to Z.
Initialization is treated identically.
Only one level of user-defined implicit conversion is legal.

 class X {… X(int);X(char*);}; class Y {… Y(int);}; class Z {… Z(X);}; // intX char*X intY XZ X f(X); Y f(Y); Z g(Z); f(1);//Error  f(X(1)) or f(Y(1)) g("str"); //Error  g(X("str"))        //g(Z("str")) g(Z(X("str")))  


C. Overloading Resolution Rules 重载解析规则

[1] Exact match;
[2] Match using promotions;
[3] Match using standard conversions;
[4] Match using user-defined conversions;
[5] Mismatch, function undeclared.
         If two matches are found at the highest level where a match is found, the call is rejected as ambiguous.

 class XX {… XX(int); } ; void h(double); void h(XX); h(1); // this is h(double),  // not h(XX)


Return type is not used in overloading resolution.
Exception : Once the types of both sides of an initialization or assignment have been determined, both types are used to resolve the initialization or assignment.
 class Real { public:   operator double();   operator int();   … }; void g(Real a) { double d=a; // adouble   int i=a;    // aint   d=1;        // adouble   i=a;        // aint }



(17)Friends P241-242 P250-252

A member function specifies three properties:
   [1] the function can access the private member
   [2] the function is in the scope of the class
   [3] the function must be called on an object 
         (It has this pointer)

A static member function has properties [1] [2].
A friend function has only property [1].  A friend can access all members of the class, but it does not belong to the class.

A. 定义友元

 class matrix; // forward declaration class vector {   friend vector operator*(matrix,vector);   double v[4]; public:   double& elem(int i);}; class matrix {   friend vector operator*(matrix,vector);   vector v[4]; public:   double& elem(int i,int j);};// multiplication of a matrix with a vector vector operator*(matrix m,vector v)  {   vector r;   for (int i=0;i<4;i++)   {     r.v[i]=0;     for (int j=0;j<4;j++)       r.v[i]+=m.v[i].v[j]*v.v[j];   }   return r; }  


A friend declaration can be placed in either private or  public part of a class.
A member function of one class can be the friend of another class.
If all member functions of one class are the friends of another class, we can declare this class as friend class of another.
 class List_iterator {  int* next(); …  }; class List {  friend class List_iterator; … };

B. Finding Friends

①A friend declaration within a class does not introduce a name into an enclosing scope.  

 class matrix{  friend class Xform;  friend matrix invert(matrix);  … }; Xform* x;   //ErrorXform和invert需要单独的声明 matrix (*p)(matrix)=invert;   //Error
②A friend class must be previously declared in an enclosing scope or defined in the non-class scope immediately enclosing the class that is declaring it a friend.
 class X { … }; namespace N {  class Y {     friend class X;     friend class Z;     friend class AE;  };  class Z { … }; } class AE { … }; // not a friend of Y
③A friend function can be declared just like friend class, or it can be found through its arguments.
 void f(matrix m) {    invert(m); }/* no invert function declaration found in the enclosing scope of f, but the compiler can find invert through the scope of matrix */  namespace N{   class X;   void f();   void h(const X&);   class X {       friend void f();       friend void h(const X&);   }; } void g(const N::X& x) {    f(); // ERROR      h(x); }


(18)Members and Friends
Use a friend or a member?
Some operations must be members – constructors, destructors,  = ,  [ ] ,  ( ) ,  -> 
An operation modifying the state of a class should be a member or a global function taking a non-const reference (or pointer) parameter.

If implicit conversion is desired for all operands of an operation,  the function implementing it must be a nonmember function taking a constant reference parameter or anon-reference parameter.
If no conversions are defined,  both OK.

 class X {    …    X(int);    int m1();    int m2() const;    friend int f1(X&);    friend int f2(const X&);    friend int f3(X); }; 99.m1() or 99.m2() // error operator.之前的操作数不会发生隐式转换 f1(99) // error 隐式转换之后生成一个临时对象,不能绑定在一个非const的左值引用上 f2(99) and f3(99) // OK 



(19)Large Objects


(20)Assignment and Initialization

(21)Subscripting

(22)Function Call

(23)Dereferencing

(24)Increment and Decrement

(25)An Example – String class


0 0
原创粉丝点击