再读C++ primer,提取重点

原创 2016年08月29日 12:49:24

C++初始化变量有好几种方法:

int a=0;

int a={0};

int a{0};

 都可以。 

如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量(任何包含了显式初始化的声明即成为定义。):

 

extern int i;   // 声明i而非定义i  

int j;          // 声明并定义j  

 

 

1引用被创建的同时必须被初始化(指针则可以在任何时候被初始化,最好也初始化,否则容易出错)。

 

2)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

它的作用是为变量起一个别名。假如有一个变量a,想给它起一个别名b,可以这样写:

int a;

int &b=a;

int *p= &a;  这里&是取地址符。表示指针p指向(本语句等价于 int *p; p= &a;)    

这就表明了ba引用,即a的别名。经过这样的声明,使用ab的作用相同,都代表同一变量 

int &b=a2;//企图使b变成a2的别名(引用)是不行的。这样是错误的。 

引用不是一个对象,所以不能定义引用的引用。

 

 

 const char *p = "hello world";     指针指向的变量不可以改变,指针本身可以改变(即指向常量的指针)

            这种情况下,以下语句是合法的:

                           char c = *p;

                           p++;

                                以下语句不合法:

                           *p = 'a';

                           p[5] = 'b';

    char *const p表示指针指向的变量可以改变,但指针本身不能改变。

                       比如          int i=4;

                                        int *const p = &i;

                               那么*p = 5;是合法的,p++是不合法的

    const char  *const p 两者都不可以改变。

理解这些声明的技巧在于,查看关键字const右边来确定什么被声明为常量 ,如果该const的右边是类型,则值是常量(底层const);如果const的右边是指针变量,则指针本身是常量(顶层const)。赋值拷贝的时候要特别注意底层const,一般来说,非常量可以赋值给常量,反之则不行。(只是赋值哦,没说改变Const)

例如const int *a = int *b;  反之不行。

 

C++ 11中引入的auto主要有两种用途:自动类型推断和返回值占位。autoC++ 98中的标识临时变量的语义,由于使用极少且多余,在C++ 11中已被删除。

auto自动类型推断,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推断,可以大大简化我们的编程工作。下面是一些使用auto的例子。

 

auto a; // 错误,没有初始化表达式,无法推断出a的类型  

auto int 10 // 错误,auto临时变量的语义在C++ 11中已不存在  

auto 10  

auto 'A' 

auto s("hello");  

vector<</span>int> vctTemp;  

auto it vctTemp.begin();  

auto ptr [](){ cout << "hello world" << endl; }; 

 

另外,在使用模板技术时,如果某个变量的类型依赖于模板参数,不使用auto将很难确定变量的类型(使用auto后,将由编译器自动进行确定)。下面是一个具体的例子。

 

template <</span>class T, class U>  

void Multiply(T t, u)  

{  

auto t*u;  

auto返回值占位,主要与decltype配合使用,用于返回值类型后置时的占位。

 

template <</span>class T, class U>  

auto Multiply(T t, u)->decltype(t*u)  

{  

typedef decltype(t*u) NewType;  

NewType *pResult new NewType(t*u);  

return *pResult;  

至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为decltype(t*u) Multiply(T t, U u),但此时模板参数tu还未声明,编译无法通过。

如果这个表达式是个函数,decltype 给出的类型为函数返回值的类型。

 

[cpp] view plaincopy

int add(int i, int j){ return i+j;  

decltype(add(5,6)) var 5;//Here the type of var is return of add() -> which is int  

非常重要的标记一下,decltype 不会执行表达式而auto会,他仅仅推论一下表达式的类型。

 


int foo(){}  

decltype( foo() x; // is an int and note that   

                     // foo() is not actually called at runtime  

 


 头文件不应包含using 声明


string s1="hello", s2="world";

string s3=s1+","+s2;

string s4 =s1+",";

string s4 =s1+s2;

而+两侧运算对象至少一个是string类型:

string s6="hello"+","+s2; //错误, 不能把字面值直接相加


基于范围的for 语句

遍历给定序列中每个元素并操作

简单例子:把string对象中的字符每行一个个的输出出来


string str("some strings");

for (auto c : str)

cout<<c<<endl;

这里使用auto 让编译器来决定c变量的类型,这里是char型 


但如果想改变string 对象中的字符值,循环变量必须定义成引用

比如将string对象字符全变为大写

string str("some strings");

for (auto &c : str)

c=toupper(c);

cout<<s<<endl;

这里toupper函数是定义在〈cctype〉头文件中的处理string中的某个特定字符的集合。



标准模板库类型vector 表对象的集合。一般用{  }初始化,不容易错。

vector 〈string〉 v1{"a","ab","abc"};

除了以下情况用()初始化

vector 〈string〉 svec(10, "hi!");   表示10个string 类型的元素,每个都被初始化为"hi!"。

vector 〈int〉svec(10);   表示10个int 类型的元素,每个都被初始化为0。

vector 〈int〉svec(10,1);   表示10个int 类型的元素,每个都被初始化为1。

如果这时使用{}

vector 〈int〉 svec{10};

表示1个int类型元素,该元素是10。

vector 〈int〉svec{10,1};   表示2个int类型元素,分别是10和1。 

它的成员函数push_back()

vector 〈int〉 v2;

for(int i=0; i!=100; ++i)

v2.push_back(i);  //依次将整数值放到v2的尾端 。。使用C++,在for循环中要习惯使用!=而不是


 

迭代器

(1) 每种容器类型都定义了自己的迭代器类型,如vector:
vector::iterator iter;这条语句定义了一个名为iter的变量,它的数据类型是由vector定义的iterator类型。
(2) 使用迭代器读取vector中的每一个元素:
vector 《int》 ivec(10,1);
for(vector::iterator iter=ivec.begin();iter!=ivec.end();++iter)
{
*iter=2; //使用 * 访问迭代器所指向的元素
}
其中begin()成员函数指向容器中的第一个元素,而end()成员函数其实指向最后一个元素的下一个元素,所以没什么真正含义,只是一个标记而已,如果v.begin()=v.end() ,则容器为空。
const_iterator:
只能读取容器中的元素,而不能修改。
for(auto it=ivec.cbegin();it!=ivec.cend();it++)
{
cout<<*it;
}
这里cbegin()和cend()是常量迭代器,是const_interator的类型,只能用于读取容器中的元素,不能修改容器中的元素。
注意,但凡是使用了迭代器的循环体,都不要想迭代器所属的容器中添加元素。

解引用操作符:所有迭代器都提供了解引用操作符(*),用于获取迭代器所指向的元素。以下代码都是合法的。


std::cout << *iter;
 
*iter = 5;
 
*iter = *iter + 5;

取后继元素操作符:所有迭代器都可以通过 iter++、++iter 操作符获取其后继元素的迭代器。

auto pbeg=v.begin();

while(pbeg!=v.end() && *pbeg>=0)  //输出元素直到第一个负值

cout<<*pbeg++<<endl;      等价于*(pbeg++)  , 解引用 返回的是pbeg未改变之前的值,然后再将指针pbeg+1。 这是一种广泛的用法。

不过注意,C++中一般更倾向于使用前置++, 因为这样先改变指针再返回指针改变后所指向的值,节省空间并且更易理解。


迭代器运算
it+n  表示迭代器所指示的位置相比原来的向右(前)移动了n个元素,
it-n  表示迭代器所指示的位置相比原来的向左(后)移动了n个元素,


数组的维度必须是一个常量表达式:
int  a[10];
int  a[var];  //除非var是constexpr,否则错误。
在C++中,数组大小固定,,对某些程序性能较好,但灵活性较差,如果不清楚元素的确切个护士,请使用vector.

不能把一个数组直接赋值给另一个数组。如  int A[]={1,1,1};  int B[]; 不能 B=A;所以数组作为函数形参传递的时候也要特别注意。

也不允许用vector对象来初始化数组。但允许使用数组来初始化vector 对象。
不存在引用的数组。
数组和指针关系密切:
string nums[] ={"one","two","three"};       //这里没声明数组的大小,但编译器会根据初始值的数量推算出为3
string *p=&nums[0];

不存在引用的数组,但存在数组的引用
int &ref[10]; 错
int (&ref)[10];对 包含10个Int元素的数组ref的引用

多维数组的初始化
允许使用花括号括起来的一组值初始化多维数组,这点和普通的数组一样。下面的初始化形式中,多维数组的每一行分别用花括号括了起来:

int ia[3][4] = {        // 三个元素,每个元素都是大小为4的数组

     {0, 1, 2, 3},       // 第1行的初始值

     {4, 5, 6, 7},        // 第2行的初始值

     {8, 9, 10, 11}       // 第3行的初始值

};

其中内层嵌套着的花括号并非必需的,例如下面的初始化语句,形式上更为简洁,完成的功能和上面这段代码完全一样:

// 没有标识每行的花括号,与之前的初始化语句是等价的

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};


程序中经常会用到两层嵌套的for循环来处理多维数组的元素:

constexpr size_t rowCnt = 3, colCnt = 4;

int ia[rowCnt][colCnt]; // 12 个未初始化的元素

// 对于每一行

for (size_t i = 0; i != rowCnt; ++i) {

    //对于行内的每一列

    for (size_t j = 0; j != colCnt; ++j) {

        // 将元素的位置索引作为它的值

        ia[i][j] = i * colCnt + j;

    }

}

外层的for循环遍历ia的所有元素,注意这里的元素是一维数组;内层的for循环则遍历那些一维数组的整数元素。此例中,我们将元素的值设为该元素在整个数组中的序号。

使用范围 for语句处理多维数组
由于在C++11新标准中新增了范围for语句,所以前一个程序可以简化为如下形式:

size_t cnt = 0;

for (auto &row : ia)            // 对于外层数组的每一个元素

    for (auto &col : row) {     // 对于内层数组的每一个元素

        col = cnt;              // 将下一个值赋给该元素

        ++cnt;                  // 将 cnt加1

}

因为要改变元素的值,所以得把控制变量row和col声明成引用类型。

标准库函数 begin和end  跟容器的bengin和end 成员函数功能差不多。不过毕竟数组不是类类型,所以不太一样。

int ia[] = {0,1,2,3,4,5,6,7};

int *beg = begin(ia); //指向ia的首元素

int *last = end(ia);  // 指向数组ia的最后一个元素的下一个位置。 


位与,位或,位异或

7&8 = 0000 0111 & 0000 1000 = 0000 0000 = 0


7|8 = 0000 0111 | 0000 1000 = 0000 1111 = 15


7^8 = 0000 0111 ^ 0000 1000 = 0000 0111 = 7

  

switch(ch){

case 'a':  ... ; break;

case 3:   .... ;  break;

}

case 后面只能是整型常量表达式(整数,字母等),不能是小数或者变量。



try
{
//程序中抛出异常
throw value;
}


catch(valuetype v)
{
//例外处理程序段
}


语法小结:throw抛出值,catch接受,当然,throw必须在“try语句块”中才有效。

int main() 

try 

cout << "在 try block 中, 准备抛出一个int数据类型的异常." << endl; 
throw 1; 

//程序如果执行到下面这句表示还没有碰到  int 的异常。
cout << "在 try block 中, 准备抛出一个double数据类型的异常." << endl; 
throw 0.5; 


catch( int& value 

cout << "在 catch block 中, int数据类型处理异常错误。”<< endl; 


catch( double& d_value 

cout << "在 catch block 中, double数据类型处理异常错误。”<< endl; 


return 0; 


http://blog.sina.com.cn/s/blog_a9303fd901018ost.html


建议函数和变量在头文件中声明,在源文件中定义。


对于不需要修改的变量,使用常量引用来传递参数可以既保证安全性又保证效率!


调用一个返回引用的函数得到左值

函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。

返回指向函数调用前就已经存在的对象的引用是正确的。

我们能为返回类型是非常量引用的函数的结果赋值:

 char &get_val (string &str, string::size_type ix)

{

return str[ix];          //返回某一位

}


int main()

{

string s("a value"); //初始化

get_val(s,0)='A';

cout<<s<<endl;

}


const_cast   

去掉const属性:const_cast 《int *》 (&num),常用,因为不能把一个const变量直接赋给一个非const变量,必须要转换。

把const的num地址转化为普通int 地址  

const int ica = 100;    

    int ia = const_cast《int &》(ica);   

 

    cout<< ia <<ica<<endl; 

都是100

使用范围:

1. 常量指针被转化成非常量指针,转换后指针指向原来的变量

  1. const int ica 100;    
  2.     int ia const_cast<</span>int *>(&ica);    
  3.     *ia 200;    
  4.     cout<< *ia <<ica<<endl;   //200 100    

2. 常量引用转为非常量引用。
3. 常量对象(或基本类型)不可以被转换成非常量对象(或基本类型)。
  1.  const int 100;  
  2.  int const_cast<</span>int>(i);  //不允许

默认实参是通过给形参表中的形参提供明确的初始值来指定的。程序员可为一个或 多个形参定义默认值。但是,如果有一个形参具有默认实参,那么,它后面所有的 形参都必须有默认实参。

既可以在函数声明中也可以在函数定义中指定默认实参,但是在一个文件中,只能为一个形参指定默认实参一次;

个人比较推荐这种格式: 在申明中将默认参数给出,在定义中不再给出!

  1. //CPerson.h  
  2. int initPerson(int old, string name "二毛"string race "汉");  
  3. //CPerson.cpp  
  4. #include "CPerson.h"  
  5. //int initPerson(int old, string name "二毛", string race "汉"){...}  
  6.   
  7. int initPerson(int old, string name, string race)        //定义
  8. {...}  


构造函数用来初始化类的对象。只要类的对象被创建,就会执行构造函数。构造函数的名字和类名相同。构造函数没有返回类型。

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

 

class CExample {
public:
    
int a;
    
float b;
    
//构造函数初始化列表
    CExample(): a(0),b(8.8)
    
{}
    
//构造函数内部赋值
    CExample()
    
{
        a
=0;
        b
=8.8;
    }

}
;

上面的例子中两个构造函数的结果是一样的。

也可以在类的外部定义构造函数

 CExample:: CExample()

  {
        a
=0;
        b
=8.8;
    }

如果没有为一个类显式地定义任何构造函数,编译器将自动为这个类生成默认构造函数。

1.什么是默认构造函数?

一个函数是默认构造函数当且仅当调用它可以不需要传入任何参数。这个函数可以是用户自定义的,也可以是编译器产生的。

2.编译器什么时候隐式声明默认构造函数?

有两个条件:

 

  • 该类没有显式声明任何构造函数。--既然你都定义了,系统就不给你生成了。
  • 1、每个类必须有一个构造函数,否则没法创建对象;
  • 2、若programer没有提供任何构造函数,则C++提供一个默认的构造函数,该默认构造函数是无参构造函数,它仅负责创建对象,不做任何初始化的工作;
    3、只要programer定义了一个构造函数(不管是无参还是有参构造),C++就不再提供默认的默认构造函数。即如果为类定义了一个带参的构造函数,还想要无参构造函数,就必须自己定义;
    4、与变量定义类似,在用默认构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的位模式全为0,否则,对象值是随机的。

 定义类时, 使用class和struct关键字的区别:

默认情况下,struct的保护级别为public,而class的保护级别为private。

友元 :优点:提高了程序的运行效率。

缺点:破坏了类的封装性和数据的透明性。


友元函数  
      友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
      friend  类型 函数名(形式参数);

class Point { 

public: Point(double xx, double yy) 

{ x=xx; y=yy; } 

void Getxy();

friend double Distance(Point &a, Point &b); 

private: double x, y; 

};

void Point::Getxy() {

cout<<"("<<<","<<Y<<")"<<endl; } 

double Distance(Point &a, Point &b)

{

double dx = a.x -b.x; double dy = a.y - b.y; return sqrt(dx*dx+dy*dy); 

}

void main() { Point p1(3.0, 4.0), p2(6.0, 8.0);

p1.Getxy(); p2.Getxy();

double d = Distance(p1, p2); 

cout<<"Distance is"<< endl; }

注意友元函数Distance的调用,像普通成员那样调用,因为它不属于类。所以不像p1.Getxy();前面要带对象.

友元类  
      友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。       
      当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
      friend class 类名;
      其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。

      例如,以下语句说明类B是类A的友元类:
      class A
      {
             
      public:
             friend class B;
             
      };
      经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。

      使用友元类时注意:
            (1) 
友元关系不能被继承。 
            (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
            (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明 

在类中,静态成员可以实现多个对象之间的数据共享

 一、静态数据成员
    在类内对成员的声明之前加上关键字static,将数据成员声明为static,即为静态数据成员。
    注意:* 静态数据成员不属于某一个对象,它是属于类的,其值为所有本类的对象所共享。
          * 静态数据成员是在所有对象之外单独开辟内存空间的。只要在类中声明了静态数据成员,
            即使没有定义对象,系统也为其分配内存空间。
          * 静态数据成员必须在类体外进行初始化,格式如下:
            数据类型  类名::静态数据成员名=初值

初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。

初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员

* 静态成员函数与普通成员函数的根本区别是,静态成员函数没有this指针。因而静态成员函数可直接访问本类中的静态数据成员,对于非静态数据成员,一定要加上“对象名.”。

非静态成员函数可以任意地访问静态成员函数和静态数据成员。

静态成员函数不能直接访问非静态成员函数和非静态数据成员,需要通过对象调用

#include

using namespace std;

class Student

{

   public:

      Student(int n=1000,float s=0):num(n),score(s){}  //构造函数

      void printinfo();   //普通成员函数

      static void printaver(Student*);  //静态成员函数,形参为Student类的指针

   private:

      int num;

      float score;

      static float total;  //静态数据成员

};

float Student::total=0;   //静态数据成员初始化


void Student::printinfo()  //定义成员函数,输出相关信息

{

    cout<<num<<"  "<<score<<endl;

}

void Student::printaver(Student* ps)  //定义静态成员函数,输出总分数及平均分

{

    int n;

    cout<<"how many students:";

    cin>>n;

    for(int i=0;i

    {

      total=total+(ps+i)->score;  引用静态数据成员及非静态数据成员的方法不同

    }

    cout<<"  total is:"<<total<<endl;

    cout<<"average is:"<<total/n<<endl;;

}


int main()

{

   Student stu[]={Student(1000,96),   //定义对象数组

                  Student(1001,92),

                  Student(1002,65)};

   for(int i=0;i<3;i++)    //循环调用printinfo()函数,输出每个学生信息

      {

        cout<<i+1<<".";

        stu[i].printinfo();

      }

   Student::printaver(stu);  //调用静态成员函数,实参为对象数组的起始地址

   return 0;

 

This  指针:

当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。


this指针只能在一个类的成员函数中调用,它表示当前对象的地址。下面是一个例子:

   void Date::setMonth( int mn ) 
    { 
     month = mn; // 这三句是等价的 
     this->month = mn; 
     (*this).month = mn; 
    } 

1. this只能在成员函数中使用。
全局函数,静态函数都不能使用this。

this在成员函数的开始执行前构造,在成员的执行结束后清除。

我们只有获得一个对象后,才能通过对象使用this指针

在以下场景中,经常需要显式引用this指针
   (1) 为实现对象的链式引用(如例1); 
   (2) 为避免对同一对象进行赋值操作(如例2);
   (3) 在实现一些数据结构时,如list.

7. 举例:

//例1:

 

#include
#include

class Person {
      public:
          typedef enum {
                 BOY = 0,
                 GIRL = !BOY
          } SexType;
      public:
             Person(char *n, int a, SexType s)
             {
                 name = new char[strlen(n)+1];  //这里的 name 等价于this->name
                 strcpy(name,n);                //这里的 name 等价于this->name
                 age = a;                       //这里的 age 等价于this->age
                 sex = s;                       //这里的 sex 等价于this->sex
             }
          

            int get_age(void) const
            
                 return age;    //这里的 age 等价于this->age
             }


             Person& add_age(int a)

             {
                 age +=a;  //这里的 age 等价于this->age
                 return *this; // 返回本对象的引用 
             }


      private:

            char *name;
            int age;
            SexType sex;
};

void TestPerson(void)
{
     Person ZhangSan("ZhangSan", 20, Person::BOY);
     printf("ZhangSan.age = %d\n", ZhangSan.get_age());
     printf("ZhangSan.add_age = %d\n", ZhangSan.add_age(1).get_age()); //增加1岁的同时,可以对新的年龄直接输出;

     return;
        

int main(void)
{
    TestPerson();
    while(1);
    
}

 

//例2:

 

#include

class Location {
     int X,Y;//默认为私有的
 public:
     void init(int x,int y) { X =x; Y = y;};
     void assign(Location& pointer);
     int GetX(){ return X; }
     int GetY(){ return Y; }
};

void Location::assign(Location& pointer)
{
    if(&pointer!=this) 

//同一对象之间的赋值没有意义,所以要保证pointer不等于this, this 是当前对象的地址,pointer是当前类对象中调用的

//另一个对象的地址参数

    {
        X=pointer.X;
        Y=pointer.Y;
    }
}

int main()
{
     Location x;
     x.init(5,4);
     
     Location y;
     y.assign(x);
     
     printf("x.X = %d, x.Y = %d \n",  x.GetX(), x.GetY());
     printf("y.X = %d, y.Y = %d ",  y.GetX(), y.GetY());
     
     while(1);
     return 0;
}


动态数组

c++要求定义数组时,必须明确给定数组的大小,要不然编译通不过 
 
  如: int Array[5];正确

       int i=5;
       int Array[i]; 错误 因为在编译阶段,编译器并不知道 i 的值是多少

   那么,我们该如何解决定义长度未知的数组呢?
   答案是:new 动态定义数组 

   因为new 就是用来动态开辟空间的,所以当然可以用来开辟一个数组空间
   
   这样,下面的语句:
    int size=50;
    int *p=new int[size]; 是正确的

最后 ,因为调用了new, 千万千万别忘记在用完之后,将其所占资源 delete 掉.delete动态数组的时候,在指针前加 []

delete []  p ;


拷贝构造函数:

下面  网址中说得很清楚

http://blog.csdn.net/lwbeyond/article/details/6202256

我摘抄一段过来

一. 什么是拷贝构造函数

首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:

  [c-sharp] view plaincopy

  1. int 100;  
  2. int a;   


而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。


用已有对象初始化新建对象

  [c-sharp] view plaincopy

  1. #include   
  2. using namespace std;  
  3.   
  4. class CExample  
  5. private 
  6.     int a;  
  7. public 
  8.     //构造函数  
  9.     CExample(int b)  
  10.     b;}  
  11.       
  12.     //拷贝构造函数  
  13.     CExample(const CExample& C)  
  14.      
  15.         C.a;  
  16.      
  17.   
  18.     //一般函数  
  19.     void Show ()  
  20.      
  21.         cout<<a<<endl;  
  22.      
  23. };  
  24.   
  25. int main()  
  26.  
  27.     CExample A(100);  
  28.     CExample A; // CExample B(A); 也是一样的  
  29.      B.Show ();  
  30.     return 0;  
  31. }   

运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的

CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量

函数调用时参数是对象

  harp] view plaincopy

  1. class CExample   
  2.  
  3. private 
  4.  int a;  
  5.   
  6. public 
  7.  //构造函数  
  8.  CExample(int b)  
  9.    
  10.   b;  
  11.   cout<<"creat: "<<a<<endl;  
  12.   
  13.   
  14.  //拷贝构造  
  15.  CExample(const CExample& C)  
  16.   
  17.   C.a;  
  18.   cout<<"copy"<<endl;  
  19.   
  20.    
  21.  //析构函数  
  22.  ~CExample()  
  23.   
  24.   cout<< "delete: "<<a<<endl;  
  25.   
  26.   
  27.      void Show ()  
  28.   
  29.          cout<<a<<endl;  
  30.       
  31. };  
  32.   
  33. //全局函数,传入的是对象  
  34. void g_Fun(CExample C)  
  35.  
  36.  cout<<"test"<<endl;  
  37.  
  38.   
  39. int main()  
  40.  
  41.  CExample test(1);  
  42.  //传入对象  
  43.  g_Fun(test);  
  44.   
  45.  return 0;  
  46.  


调用g_Fun()时,会产生以下几个重要步骤:
(1).test对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这两个步骤有点像:CExample C(test);
(3).等g_Fun()执行完后, 析构掉 C 对象。


a;  
  b=a;   //拷贝构造函数调用  
 //或  
  b(a);   //拷贝构造函数调用  
 ///////////////////////////////////     
  a;  
  b;  
 b =a;   //赋值运算符调用  

 

你只需要记住,在C++语言里,  
 String   s2(s1);  
 String   s3    s1;  
 只是语法形式的不同,意义是一样的,都是定义加初始化,都调用拷贝构造函数。

相关文章推荐

再读c++ primer

再读c++ primer 1.        extern的使用 是当在a.cpp文件中想使用b.cpp文件中的全局变量extern_data的时候,当在b.cpp中定义了extern_data,a...

再读c++ primer2

1.        通过指针赋值和指针赋值,这个概念一定要区分开来 通过指针赋值:表示是更新指针所指向的内存区域的值。 给指针赋值:表示更改指针所指向的地址,是指针指向另外一个地址。 int a...

C++Primer再读读书笔记:第一、二章

过去也看过一遍,不过当时没怎么详细理解,在此阅读一遍查漏补缺,将基础的回味一下。记录下点滴。  1、变量初始化:      定义变量时,应该给变量赋初始值,除非确定...

《C++ Primer Plus》第6版重点笔记

下面是我看《C++ Primer Plus》第6版这本书后所做的笔记,作为备忘录便于以后复习。 笔记部分 C++的const比C语言#define更好的原因? 首先,它能够明确指...

C++ Primer Plus第6版18个重点笔记

下面是我看《C++ Primer Plus》第6版这本书后所做的笔记,作为备忘录便于以后复习。笔记部分C++的const比C语言#define更好的原因?首先,它能够明确指定类型,有类型检查功能。其次...

C++ Primer Plus第6版重点笔记

下面是我看《C++ Primer Plus》第6版这本书后所做的笔记,作为备忘录便于以后复习。 笔记部分 C++的const比C语言#define更好的原因? 首先,它能够明确指...
  • tham_
  • tham_
  • 2015年09月21日 14:19
  • 531

【读书笔记】【C++ Primer Plus第6版】C++ Primer Plus第6版的18个重点笔记

原址:http://blog.csdn.net/lanxuezaipiao/article/details/41622351 下面是我看《C++ Primer Plus》第6版这本书后所做的笔记,作...

scala&nbsp;重点语法总结

Scala 有两种变量, val 和 var  其中val不可变,var可变 val msg: String = "Hello yet again, world!" 或者类型推断 val msg =...

熄灯之后的学习——再读《MySQL必知必会》(2)

命令用 ; 结束,仅仅按 Enter 不执行命令 输入 help 或 \h 获得帮助 输入 quit 或者 exit 退出命令行程序 关键字(key word):作为 MySQL 语言组成部分的一个保...

熄灯之后的学习——再读《MySQL必知必会》(5)|| 过滤数据

搜索条件(过滤条件): 只检索所需数据需要指定的。 where子句在表名 (from子句)之后给出,在同时使用 order by 和 where 子句的时候,应该让 order by 位于 whe...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:再读C++&nbsp;primer,提取重点
举报原因:
原因补充:

(最多只允许输入30个字)