c++基础

 1.构造函数初始化
Animal::Animal():aget(1),maxLevel(30)
{
}
2.除非友元,否则不允许用户访问类中的私有数据成员。
  friend void fun(Animal& a);
  void fun(Animal& a)
  {
   cout<<a.name<<endl;
  }
3.如果参数是指针,且仅作输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。
例如:
void StringCopy(char *strDestination,const char *strSource);
 如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。

4.函数的返回值是一个对象

如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。
例如:
class String
{…
 // 赋值函数
 String & operate=(const String &other); 
// 相加函数,如果没有friend修饰则只许有一个右侧参数
friend String   operate+( const String &s1, const String &s2);
private:
 char *m_data;
}

 String的赋值函数operate = 的实现如下:
String & String::operate=(const String &other)
{
 if (this == &other)
  return *this;
 delete m_data;
 m_data = new char[strlen(other.data)+1];
 strcpy(m_data, other.data);
 return *this; // 返回的是 *this的引用,无需拷贝过程
}

对于赋值函数,应当用“引用传递”的方式返回String对象。如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。例如:
 String a,b,c;
 …
 a = b;   // 如果用“值传递”,将产生一次 *this 拷贝
 a = b = c;  // 如果用“值传递”,将产生两次 *this 拷贝

 String的相加函数operate + 的实现如下:
String  operate+(const String &s1, const String &s2) 
{
 String temp;
 delete temp.data; // temp.data是仅含‘\0’的字符串
  temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];
  strcpy(temp.data, s1.data);
  strcat(temp.data, s2.data);
  return temp;
}

对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。例如:
 c = a + b;
此时 a + b 并不返回期望值,c什么也得不到,流下了隐患。

5.引用与指针的比较

 

引用是C++中的概念,初学者容易把引用和指针混淆一起。一下程序中,n是m的一个引用(reference),m是被引用物(referent)。
 int m;
 int &n = m;
n相当于m的别名(绰号),对n的任何操作就是对m的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。
引用的一些规则如下:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
 以下示例程序中,k被初始化为i的引用。语句k = j并不能将k修改成为j的引用,只是把k的值改变成为6。由于k是i的引用,所以i的值也变成了6。
 int i = 5;
 int j = 6;
 int &k = i;
 k = j; // k和i的值都变成了6;
 上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。
 以下是“值传递”的示例程序。由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n, 所以n的值仍然是0。
 void Func1(int x)
{
 x = x + 10;
}

int n = 0;
 Func1(n);
 cout << “n = ” << n << endl; // n = 0
 
以下是“指针传递”的示例程序。由于Func2函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变,所以n的值成为10。
 void Func2(int *x)
{
 (* x) = (* x) + 10;
}

int n = 0;
 Func2(&n);
 cout << “n = ” << n << endl;  // n = 10

 以下是“引用传递”的示例程序。由于Func3函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等于改变n,所以n的值成为10。
 void Func3(int &x)
{
 x = x + 10;
}

int n = 0;
 Func3(n);
 cout << “n = ” << n << endl;  // n = 10

 对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?
答案是“用适当的工具做恰如其分的工作”。
 指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?
如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。

 

6. && 左右两边均为true 时为true , || 左右两边一个为true 即为 true.
   false 在算术表达式中为0, true 为1.
7. 算术运算优先级
   逻辑运算符号 NOT
   算术运算符(*,/,%)
   算术运算符(+,-)
   相对关联运算符 (== ,!=) 
   逻辑运算符号 AND
   逻辑运算符号 OR
   赋值运算符
8. continue 终止循环的current iteration
   string word;
   const int min_size = 4;
   while (cin >>word)
   {
     if(word.size()<min_size)
      //结束此次迭代
     continue;
     //程序执行到此,则输入长度都大于或等于 min_size 个字符
     process_text(word);
   }
9. const int seq_size = 18;
   #include<vector>
   vector<int>pell_seq(seq_size)  // pell_sql 定义为一个vector object,可储存18个int元素,每个元素初值为0.
   for(int ix= 2;ix< seq_size; ++ix)
   pell_sql[ix] = pell_seq[ix-2] + 2*pell_seq[ix-1];
   // vector 初始化
    vector<int>elem_seq(sql_size);
    elem_sql[0] = 1;
    elem_sql[1] = 2;
   或:
     利用一个已初始化的array 作为 vector 的初值

   int elem_vals[seq_size] = {
   1,2,3
  };
    vector<int>elem_seq(elem_vals,elem_vals+seq_size);

 


9.初始化:
  int ival(1024)  直接初始化 更灵活且效率更高
  int ival = 1024 复制初始化
10.extern
  extern int i;声明不是定义
  extern int i=65535 ;定义
 程序中变量可以声明多次,但只能定义一次。

11:
  引用:
  引用必须用与该引用同类型的对象初始化.
   int ival = 1024;
   int &refVal = ival ; //ok:refVal refers to ival
   int &refVal2 ; // error:a reference must be initialized
   int &refVal3 = 10 ; // error: initalizer must be an object
  const 引用: 指向const对象的引用:
   const int ival = 1024 ;
   const int &refVal = ival ; //ok:both reference and object are const;
   int &ival = ival ;//error:non const reference to a const object;
   可以读取但不能修改refVal;
  const 引用可以初始化为不同类型的对象或者初始化为右值。
    int i= 42;
    const int &r = 42 ; // legar for const references only
    cont  int &re = r+i;

   double dval = 3.14 ;
   const int &ri = dval;
   编译器会转成如下形式:
   int temp  = dval;
   const  int &ri = dval;
   
 //非const 引用只能绑定到与该引用同类型的对象。
   const 引用则可以绑定到不同但相关的类型的对象或绑定到右值。

12:  枚举
 
 枚举成员值可以是不唯一的:
  enum  Points{point2d = 2, point2w, point3d = 3, point3w};
  point2w默认初始化,为3.  point3w 为4.
 枚举对象的初始化或赋值,只能通过其枚举成员或同一枚举类型的其他对象进行。
  Points pt3d = point3d; // ok:point3d is a points enumerator;
  Points  pt2w = 3 ; // error: pt2w initialized whit int
  pt2w =  polygon ; // error: polygon is  not a Points enumerator
  pt2w  = pt3d ;// ok: both are objects of points enum type
  把3赋给Points 对象是非法的,即使3与一个points枚举成员相关联。

13: 类类型
   用class 和struct 关键字定义类的唯一差别在于默认访问级别,默认情况下,
struct 的成员为public,而class的成员为private;


14: 头文件用于声明而不是用于定义
    extern int ival = 10;  //  initializer, so it's a definition
    double fica_rate  ;    // no extern ,so it's a definition
    因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。


15: 避免多重包含
  
#ifndef XXX_H
#define XXX_H
#endif


16: string

  string 对象的定义和初始化
  string s1;  //默认构造函数,s1为空串
  string s2(s1);  //将s2 初始化为s1的一个副本。
  string s3("value"); //将s3初始化为一个字符串字面值副本
  string s4(n,'c') ;  //将s4初始化为字符'c'的n个副本
  警告:  字符串字面值与标准库string 类型不是同一种类型.

  *和字符串字面值的连接:
  当进行string 对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少一个是string类型。  string s1 = "hello" ;
  string s2 =  "world";
  string s3 = s1+",";  //ok:adding a string and literal
  string s4 = "hello"+"," ; //error: no string operand
  string s5 = s1+ "," +" world"; //ok:each+ has string operand
  string s6 = "hello"+","+s2; //error:cant add string literals ;
  *从string 对象获取字符
  string str("some string");
  for(string::size_type ix = 0 ;ix != str.size(); ++ix)
  cout << str[ix] << endl;
  * 下标操作可用作左值
   for(string::size_type ix = 0; ix != str.size(); ++ix)
   str[ix] = '*';
    *cctype 头文件中的字符处理
   isalnum(c)字母或数字
   isalpha(c) 字母
   iscntrl(c) 控制字符
   isdigit(c) 数字
   isgraph(c) 不是空格,但可以打印
   islower(c) 小写字母
   isprint(c) 可打印的字符
   ispunct(c) 标点符号
   isspace(c) 空白字符
   isupper(c) 大写字母
   isxdigit(c) 十六进值
   tolower(c) 大写字母,则返回其小写字母,否则直接返回c
   toupper(c) 小写字母,则返回其大写字母,否则直接返回c
// 给定对象中标点符号的个数
string s("hello word!!!");
string::size_type punct_cnt = 0;
for(string::size_type index=0; index != s.size();++index)
if(ispunct(s[index]))
  ++punct_cnt;
cout << punct_cnt;

17:  标准库vector 类型 

   vector(容器)是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。
   vector不是一种数据类型,而只是一个类模板,可以来定义和初始化vector对象。

  几种初始化vector对象的方式
  vector<T>  v1;  vector保持类型为T的对象,默认构造函数v1为空。
  vector<T>  v2(v1) ; v2 是v1的一个副本。
  vector<T>  v3(n,i); v3包含n个值为i的元素.
  vector<T>  v4(n) ;  v4含有值初始化的元素的n个副本。
  1.创建确定个数的元素。
 如果复制,两个vector必须保存同一种元素类型
  vector<int> ivec1 ; // ivec holds objects of type int
  vector<int> ivec2(ivec1);//ok:copy elements of ivec1 into ivec2
  vector<string> svec(ivec1); //error:svec holds strings, not  ints

   vector<int> ivec4(10,-1); // 10 elements ,each initialized to -1
   vector<string> svec(10,"hi!"); // strings, each initialized to "hi"
   2.值初始化
   vector<int> fvec(10);   // 10 elements ,each initialized to 0
   vector<string> svec(10);// 10 elements, each an empty string
  vector  对象的操作  
  1.vector 对象的size 
     vector<int>::size_type  //ok
     vector::size_type  // erro
   size_type:必须指出该类型是在那定义的.
  2. 向vector 添加元素
    string word; 
    vector<string> text;
    while( cin >> word)
     {
     text.push_back(word);
     }
  3. vector 的下标操作
    for(vector<int>::size_type  ix = 0;ix != ivec.size(); ++ix)
         ivec[ix] = 0;
  4. 下标操作不添加元素 
     vector<int> ivec;
     for( vector<int>::size_type ix = 0 ; ix != 10 ; ++ix)
       ivec[ix] = ix;
      下标操作只能获取已存在的元素
    正确写法:
        for( vector<int>::size_type ix = 0 ; ix != 10 ; ++ix)
         ivec.push_back(ix);

   警告:只能对确知已存在的元素进行下标操作
           vector<int> ivec;  //empty vector
           cout<< ivec[0]     //error: ivec has no elements !

           vector<int> ivec2(10);  //vector with 10 elements
           cout<< ivec[10] ;       //error: ivec has elements 0...9

18. 迭代器简介

  1. 容器的iterator 类型  
    vector<int>::iterator iter;
  2. begin 和end  操作
   vector<int>::iterator iter = ivec.begin();
  3. 解引用运算
   迭代器类型可以使用解引用操作符(*操作符)来访问迭代器所指向的元素。
    *iter = 0;
  4. 示例
  下标操作法:
    for( vector<int>::size_type ix = 0 ; ix != ivec.size() ; ++ix)
     ivec[ix] = 0;
  迭代器:
    for(vector<int>::iterator iter = ivec.begin(); iter !=ivec.end();++iter)
   *iter = 0;  5. const_iterator
   5.只读容器内元素。
    for(vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++iter)
   cout<< *iter <<endl;
   6. 不要把const_iterator 对象与const 的iterator 对象混淆起来,声明一个const 迭代器时,必须初始化迭代器,一但初始化,就不能改变它的值。
    vector<int> nums(10) ;// nums is nonconst;
    const vector<int>::iterator cit = nums.begin();
    *cit = 1;  // ok: cit can change its underlying element
    ++cit;     // error: can't change the value of cit
   7. const_iterator 对象可以用于const vector 或非const vector.
     const vector<int> nines(10,9); // cannot change elements in nines
     // error: cit2 could change the element it refers to  and nines is const
     const vector<int>::iterator cit2 = nines.begin();

    //ok: it can't change an element value , so it can be used with a      const vector<int>
      vector<int>::const_iterator it = nines.begin();
     *it = 10 ; // error:*it is const ;
     ++it;      // ok: it isn't const so we can change its value
    注解: 
   //an iterator that cannot write elements
    vector<int>::const_iterator
  // an iterator whose value cannot change
   const vector<int>::iterator
   8. 迭代器的算术运算
     1. iter + n
        iter - n
       位置在所指元素之前或之后(+,-)n个元素的位置。
     2. iter1 - iter2  difference_type 的signed 类型的值.
        定位于 vector 的中间元素
       vector<int>::iterator mid = vi.begin()+ vi.size()/2
      任何改变vector长度的操作都会使已存在的迭代器失效,列如,在调用push_back 之后,就不能再信指向vector的迭代器的值了。
 

19.  标准库  bitset 类型
     处理二进值位的有序集。
     #include<bitset>
     using std::bitset;
   1.定义和初始化
     bitset<32> bitvec ; // 32bits ,all zero
    bitset<n> b ; // b有n位,每位都为0
    bitset<n> b(u) ; //b是unsigned long 型u的一个副本
    bitset<n> b(s); //b是string对象s中含有的位串的副本
    bitset<n> b(s,pos,n) //b是s中从位置pos开始的n个位的副本
  2.用unsigned 值初始化bitset对象
   当用unsigned long 值作为bitset对象的初始值时,该值将转化为二进值的位模式。而bitset对象中的位集作为这种模式的副本.如果bitset长度大于unsigned long 值的二进制位,则其余高阶位将置为0,如果小于,则只用低阶位,超过bitset的高阶位将被丢弃。 
   十六进制0Xffff 表示为二进制就是十六个1和十六个0(每个0xf可表示为1111),可以用0xffff初始化bitset对象。
   //bitvec1 is smaller than  the initializer
   bitset<16> bitvec1(0xffff);  // bits 0...15 are set to 1
   //bitvec2 same size as initializer
   bitset<32> bitvec2(0xfffff); // bits 0...15 are set to ; 16...31 are 0
  // on  a 32-bit machine ,bits 0 to 31 initialized from 0xffff
   bitset<128> bitvec3(0xfffff);//bits 32 through 127 initialize to zero
   3.用string 对象初始化bitset对象
   从string读入位集的顺序从右向左。
   string strval("1100");
   bitset<32> bitvec4(strval); 
   bitvec4 的位模式中第2和3的位置为1,其余为0。如果string 对象的字符个数小于bitset类型长度,则高阶位将置为0 .

  string str("111111000011110000");
  bitset<32> bitvec5(str,5,4);// 4 bits starting at str[5],1100
  bitset<32> bitvec6(str,str.size()-4);// use last 4 characters

  4. bitset  对象上的操作
    b.any() 是否存在置为1的二进制
    b.none() 不存在置为1的二进制位吗?
    b.count()  置为1的二进制位的个数
    b.size()   二进制的个数
    b[pos]    访问b中在pos处的二进制位
    b.test[pos]  b中在pos处的二进制位是否为1
    b.set()   所有二进制位都设置为1
    b.set(pos)  把b中在pos 处二进制置为1
    b.reset()   把b中所有二进制都置为0
    b.reset(pos)  把b中在pos 处的二进制位置为0
    b.flip()     二进制逐位取反
    b.flip(pos)    pos 处取反
    b.to_ulong()  用b中同样的二进制返回一个unsingned long 值
    os << b  把b中的位集输出到 os 流


    数组和指针


   

   

 

   
 
   
    
   


  

 

 

 
  

  
  
 

 

 

posted on 2009-03-30 16:25  alon  阅读(184)  评论(0编辑  收藏  举报

导航