c++primer plus 第六版 第八章重点内容总结 以及编程题答案

本文总结了C++ Primer Plus第六版中的关键概念,包括内联函数的使用及其提高程序运行速度的原理,引用作为变量别名和函数参数的优势,以及如何按引用传递保持数据的原样性。此外,讨论了函数的默认参数、函数重载、函数模板的重要特性,以及模板的具体化和实例化。文中还提及了decltype关键字的用途和C++11的后置返回类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.内联函数

 

为提高程序运行速度,不必在调用函数时跳到另一个位置执行代码再跳回来。采用内联函数,内联函数的编译代码与其他程序代码“内联”起来,即编译器将使用相应的函数代码替换函数调用。速度提高,但内存开销变大。

 

注意在函数声明之前+inline,在函数定义前+inline。通常做法是省略原型,将整个定义放在本应提供原型的地方。(内联不能使用递归,有些编译器可能没有这种特性)。一般来说冗长的代码段函数不大适宜用作内联函数。

 

注意c语言中的#define宏定义是通过文本替换(而不是传递参数)实现的,如#define SQUARE(X) X*X

若提供5,结果是5*5.提供4.5+7.5,结果是4.5+7.5*4.5+7.5.若提供c++,结果是c++*c++.

 

2.引用变量

 

引用是已定义的变量的别名,主要用途是作函数的形参。将引用变量作为参数,函数将使用原始数据而不是其副本。&除了取地址符,也用来声明引用。如int rats;int &redents=rats;

注意这是将redents作为rats的别名。此时&作为类型标识符的一部分。Int &代表是指向int的引用。上述引用允许将rats和rodents互换,它们指向相同的值和内存单元。即cout<<rats<<rodents; 是相同的一个变量。注意必须在声明引用时将其初始化。指针可以先声明再赋值。

 

引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来就将一直效忠于它。即int &rodents=rats;与 int *const pr=&rats;类似。如果通过赋值给rodent一个新值(新变量),那么rodent和rat的值变成了新值,但它们的地址还是原来的地址,与新值的地址并不一样。又或者引用绑定在一个指针上,即使指针变化指向其他值,引用指向的还是原值。

 

3.将引用用作函数参数

 

引用被当作函数参数时,函数中的变量名成为调用程序中变量的别名。按引用传递允许被调用函数可以访问调用函数中的变量。按值传递是两个变量,两个名称。按引用传递是一个变量两个名称。引用作形参,调用时直接给变量,相当于把形参初始化为传递的实参,指针做形参,调用时用&给出地址。当意图让函数使用传递给它的信息而不需修改时,则应使用传值,但若要想用引用,那么应该使用const限定,如double( const double &ra);另外当数据比较大时,引用参数将比较有用。注意传递引用的限制更加严格,如果ra是一个变量的别名则实参应是该变量。(x+3就不行)

 

在当前情况下,实参与引用参数不匹配,仅当参数为const引用时,c++将生成临时变量。即对于形参是const引用的c++函数,如果实参不匹配,则其行为类似按值传递,为确保原始数据不被修改,将使用临时变量来存储值。如果函数调用的参数不是左值(变量,数组元素,结构成员,引用和解除引用的指针,非左值包括字面常量,用引号括起来的字符串除外,它们由其地址表示)或者与相应的const引用参数类型不匹配的,则c++将创建类型正确的匿名变量。将函数调用的参数的值传给该匿名变量并让参数来引用该变量。

 

4.将引用用于结构

 

函数原型还是一样,如void set_pc (free_throws &ft); 如果不希望函数修改传入的结构可使用const。void display(const free_throw &ft); 初始化结构时,如果指定的初始值比成员少,余下的成员将被设置为0. 如果函数返回的是引用那么可以进行这样的赋值,accumulate(dup,five)=four;传统的返回机制与按值传递函数类似,计算return后面的表达式并将结果返回给调用函数。也就是说这个值被复制到一个临时位置,而调用程序将使用这个值。如果函数返回一个结构,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup(变量)。但在返回引用时,将直接把返回值复制到dup。

 

注意要避免返回指向函数内部临时变量的引用,最简单的方法则是返回一个作为参数传递给函数的引用,作为参数的引用将指向调用函数使用的数据。另一种方法是使用new来分配新的存储空间。使用引用可以完成类似工作如

const free_throw & clone(free_throw & ft)

{

   free_throws *pt; *pt=ft; return *pt;

}

 

5.将引用用于类对象

 

将类对象传递给函数时通常做法是使用引用,让函数将类string,ostream,istream,ofstream和ifstream等类的对象作为参数。注意string类允许使用char *的c-风格字符串来初始化string对象。

 

类型为const引用的形参的一个属性是:假设实参的类型与引用参数类型不匹配但可被转化为引用类型,程序将创建一个正确类型的临时变量,使用转换后的实参值来初始化它,然后传递一个指向该临时变量的引用。可以将实参char *(如引号括起来的字符串字面量,空字符结尾的char数组或指向char的指针变量),const char*传递给形参const string&。坚决不能返回局部变量的引用!!!

 

6.对象,继承和引用

使得能够将特性从一个类传递给另一个类的语言特性叫做继承。派生类对象可以使用基类特性,如方法。基类引用可以指向派生类对象,而无需进行强制转换。即可以定义一个接受基类引用作为参数的函数,调用该函数时可以将基类对象,和派生类对象作为参数。

 

格式化输出:setf(ios_base::fixed)将对象置于使用定点表示法,即原始格式输出,不适用科学计数法。,setf(ios_base::showpoint)显示小数点模式,precision()显示多少位小数.width()设置下一次输出操作的字段宽度,只在下一个值有效然后将恢复到默认设置。默认字段宽度为0,意味着刚好放下显示的内容。可以使用

ios_base::fmtflags initial;   //存储有效的格式化操作

initial=os.setf(ios_base::fixed); …        //操作

os.setf(initial);//重新调用所有有效操作。

7.默认参数

 

可以在函数原型中设置默认值,如char * left(const char * str, int n=1);对于代参数列表的函数,必须从右向左添加默认值,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。实参按从左到右的顺序依次被赋给相应的形参而不能跳过任何参数。因此

Beeps=harpo(3, ,8)是不允许的。默认参数可以实现以不同数目的参数调用同一个函数。

 

8.函数重载

 

函数多态(函数重载)能够使用多个同名的函数,它们完成相同的工作但是用不同的参数列表。C++使用上下文来确定要使用的重载函数版本。函数重载的关键是函数的参数列表——函数特征标。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则其特征标相同。C++允许定义名称相同的函数,条件是它们的特征标不同。从编译器角度而言,把类型引用和类型本身视为同一个特征标。注意,是特征标,而不是函数类型使得可以对函数进行重载。如下面两个声明是互斥的

Long gronk(int n,float m);         double gronk(int n,float m);仅仅如此是不允许重载的。

函数类型可以不同,但特征标必须不同。

 

小逻辑:返回正数的前几位代码段设计思路:n/=10;每除以10就可以去掉一位。知道有几位要返回几位代码完整如:

ct=digits-ct; while(ct--){num/=10;} return num;

 

9.函数模板         

 

函数模板使用泛型来定义函数,其中的泛型可用具体类型替换,通过将类型作为参数传递给模板,使编译器生成该类型的函数。函数模板:

Template<typename hehehe>

Void swap(hehehe &a, hehehe &b)

{

Hehehe temp;

temp=a; a=b; b=temp;

} 也可以使用class代替typename。调用时直接使用swap(x,y)。注意最常见的是将模板放在头文件中,并且在需要使用模板的文件中包含头文件。

 

重载的模板:

被重载的模板的函数特征标必须不同。注意在模板函数的参数中,可以存在具体类型,不必全是模板类型。

显示具体化:

当想交换两个结构中的部分成员,而不是简单交换两个结构的情况下,以上模板代码将不再适用,此时可以进行一个具体化函数定义,即显示具体化。

第三代具体化规则:

对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本。

显示具体化的原型和定义以template<>打头,并通过名称来指定类型。

匹配时,显示具体化优先于常规模板,而非模板函数优先于具体化和常规模板。

交换结构部分成员的三种原型

Void swap(job &,job &);

Template<typename T>

Void swap(T &,T&);

Template<>

Void swap<job>(job &,job &);

 

实例化和具体化:

模板本身只是一个函数定义的方案,只有在调用时才生成模板实例。Swap(i,j)是一个隐式实例化,还可以显式实例化,swap<int>(),声明时template void swap<int>(int,int);编译器将使用swap模板生成一个使用int类型的实例。记住与显示具体化之间的区分:

Template<> void swap(int &,int&) ;或者template<>void <int>(int &,int&);

隐式实例化、显示实例化和显示具体化统称为具体化。以下代码段总结了三种异同。

Template <class T>

Void swap(T&,T&);

 

Template<>

Void swap<job>(job&,job&);//显示具体化,用另一个模板函数来实现结构成员的交换。

 

Int main()

{

   Template void swap<char>(char&,char&);//显示实例化声明,一般在main内部

   Short a,b;

Swap(a,b);//通用模板隐式实例化

Job n,m;

Swap(n,m);//使用显示具体化模板函数

Char g,h;

Swap(g,h);使用显示实例化一个char型的函数版本。

}

 

编译器选择使用哪个函数版本的原则:

1.创建候选函数列表,其中包含与被调用函数名称相同的函数和模板函数(按名匹配)

2.使用候选函数列表创建可行函数列表,这些都是参数数目正确的函数,为此有一个隐式转换的序列,其中包括实参类型与相应的形参类型完全匹配的情况。

3.确定是否有最佳的可行函数。如果没有则函数调用出错。

 

在确定最佳时原则:

1.完全匹配,但常规函数优先于模板

2.提升转换(char和short自动转换为int,float自动转化为double)

3.标准转换(int转换为char,long转换为double)

4.用户定义的转换,如类声明中定义的转换。

 

当完全匹配不止一个时:

进行完全匹配时,c++允许一些“无关紧要的转换”,当完全匹配不止一个时,编译器将无法完成重载解析过程,如果没有最佳函数,那么会生成ambiguous的错误。然而有时候两个函数都是完全匹配,还是可以完成重载解析。首先指向非const数据的指针和引用优先与非const指针和引用参数匹配。但是单纯的const变量或者非const变量在参数匹配上并无先后之分。其次,非模板函数的匹配优先于模板函数(包括显示具体化)。并且当都是模板函数的情况下,那么较具体的模板函数优先。即显示具体化将优于隐式模板实例。

 

decltype关键字:

decltype (expression) var;为确定类型,编译器必须遍历一个核对表。

1.如果expression是一个没有括号括起来的标识符,则var的类型与该标识符的类型相同,包括const限定符。

2.如果expression是一个函数调用,则var的类型与函数的返回类型相同(通过查看原型得知)。

3.如果expression是一个左值,则var指向其类型的引用。注意此时expression是有括号的,因为第一步已经将没有括号的做出判断。

4.如果前面的条件都不吻合,则var 的类型与expression 的类型相同。

另结合使用typedef和decltype:

template <class T1,class T2>

void ft(T1&x, T2 &y)

{

   typedef decltype(x+y) xytype;

   xytype xpy=x+y;

   xytype arr[10];

   xytype & rxy =arr[2];

}

 

C++11的后置返回类型:

Template<class T1,class T2>

Auto gt(T1 X,T2 Y)->decltype(x+y)

{

return x+y;

}

#include<iostream>
#include<cctype>
#include<cstring>
using namespace std;
//++++++++++++++++++++++++++1++++++++++++++++++++++++++++
/*const int size=200;
void print_str(const char *,int);
int main()
{
   char s[size];
   cout<<"enter your str"<<endl;
   cin.getline(s,size);
   //cin.get();
   int num;
   cout<<"enter your num"<<endl;
   cin>>num;
   cin.get();
   print_str(s,num);
   return 0;
}
void print_str(const char *str,int n=0)
{
   cout<<" cout str "<<*str<<" n "<<n<<"times"<<endl;
   if(n>1)
   {
      n--;
      print_str(str,n);   
   }
   
}*/
//++++++++++++++++++++++++2+++++++++++++++++++++++++++++++
/*const int size=30;
struct candybar
{
   char name[size];
   double weights;
   int hots;
};
void set_st(candybar &,const char *,double,int);
void show_st(const candybar &);
int main()
{
candybar *pt=new candybar;
char *s;
double w;
int h;
cout<<"enter your string"<<endl;
cin.getline(s,size);
cout<<"enter your weights"<<endl;
cin>>w;
cin.get();
cout<<"enter your hots"<<endl;
cin>>h;
cin.get();
set_st(*pt,s,w,h);
show_st(*pt);
return 0;
}

void set_st(candybar &candy,const char *str="Millennium Munch",double weight=2.85,int hot=350)
{
   cout<<"enter your name" <<endl;
   //cin.getline(*str,size);
   cout<<"enter your weights"<<endl;
   //cin>>weight;
   cout<<"enter your hot"<<endl;
   //cin>>hot;
   strcpy(candy.name, str);
   candy.weights=weight;
   candy.hots=hot;
}

void show_st(const candybar &candy)
{
   cout<<candy.name<<endl;
   cout<<candy.weights<<endl;
   cout<<candy.hots<<endl;
}*/
//++++++++++++++++++++++++++3++++++++++++++++++++++++++++++++++++++++
/*void upper(string &);
int main()
{
   string a;
   cout<<"enter a string(q to quit)"<<endl;
   getline(cin,a);
   while(a!="q"&&a!="Q")
   {
      upper(a);
      cout<<a<<endl;
      cout<<"next string(q to quit)"<<endl;
      getline(cin,a);
   }
}

void upper(string &str)
{
   for(int i=0;i<str.size();i++)
   {
      if(islower(str[i]))
         str[i]=toupper(str[i]);
   }
}*/
//++++++++++++++++++++++4++++++++++++++++++++++++++++++++++++++++++
/*struct stringy {
    char *str;
    int ct;
};
void set(stringy &in_stringy, char * in_string)
{
    char *pt;
    int string_length = strlen(in_string);
    pt=new char(string_length+1);
    //in_stringy.str = new char(string_length + 1);
    //strcpy(in_stringy.str, in_string);
    strcpy(pt,in_string);
    in_stringy.str=pt;
    in_stringy.ct = string_length;
}
void show(const stringy &in_stringy, int print_times = 1)
{
    for (int i = 0; i < print_times; i++)
    {
        cout << "member string of struct stringy: " << in_stringy.str << endl;
    }
}
void show(const char * str, int print_times = 1)
{
    for (int i = 0; i < print_times; i++)
    {
        cout << "Print char string: " << str << endl;
    }
}
int main()
{
    stringy beany;
    char testing[] = "Reality isn't what it used to be.";

    set(beany, testing);
    show(beany);
    show(beany, 2);
    testing[0] = 'D';
    testing[1] = 'u';
    show(testing);
    show(testing, 3);
    show("Done!");

    return 0;
}*/
//+++++++++++++++++++++++++++++++++++5++++++++++++++++++++++++++++++++
/*template<class T>
T max5(const T arr[5]);
int main()
{
   int a[5]={1,2,3,4,5};
   double b[5]={1.11,2.22,3.33,4.44,5.66};
   int x=max5(a);
   double y=max5(b);
   cout<<x<<"   "<<y<<endl;
   return 0;
}
template<class T>
T max5(const T arr[5])
{
   T temp=0;
   for(int i=0;i<5;i++)
   {
      if(temp<arr[i])
         temp=arr[i];
 
   }
   return temp;
}*/
//+++++++++++++++++++++++++++++++++++++6++++++++++++++++++++++++++++++++++
/*template<class T>
T maxn(T arr[],int n);
template<>
char *maxn(char *arr[],int n);
int main()
{
   int a[6]={1,2,3,4,5,6};
   double b[4]={1.2,2.3,4.5,6.7};
   char *str[5]={"a","bb","ccc","dddd","eeeee"};
   int x=maxn(a,6);
   double y=maxn(b,4);
   char *z=maxn(str,5);
   cout<<x<<"   "<<y<<"   "<<z<<endl;
   return 0;
}

template<class T>
T maxn(T arr[],int n)
{
   T temp;
   for(int i=0;i<n;i++)
   {
      if(temp<arr[i])
         temp=arr[i];

   }
   return temp;
}
template<>
char *maxn(char *arr[],int n)
{
   char *a=arr[0];
   for(int i=1;i<n;i++)
   {
      if(strlen(a)<strlen(arr[i]))
         a=arr[i];
   }
   return a;
}*/
//+++++++++++++++++++++++++++++++++++++++7++++++++++++++++++++++++++++++++++++
/*template <typename T>
T SumArray(T arr[], int n);

template <typename T>
T SumArray(T * arr[], int n);

struct debts {
    char name[50];
    double amount;
};

int p8_7(void)
{
    int thing[6] = { 13, 31, 103, 301, 310, 130 };
    int int_sum = 0;
    struct debts mr_E[3] = 
    {
        {"Ima Wolfe", 2400.0},
        {"Ura Foxe", 1300.0},
        {"Iby Stout", 1800.0}
    };
    double *pd[3];
    double double_sum = 0.0;

    for (size_t i = 0; i < 3; i++)
    {
        pd[i] = &mr_E[i].amount;
    }

    int_sum = SumArray(thing, 6);
    double_sum = SumArray(pd, 3);

    cout << "Sum of int array: " << int_sum << endl;
    cout << "Sum of double* array: " << double_sum << endl;

    return 0;
}

template <typename T>
T SumArray(T arr[], int n)
{
    // 下面的sum初始化,在便宜的时候会有warning提示,有个简单的方法可以解决,那就是将sum初始化为arr的第一个元素,然后for循环从i=1开始
    T sum = 0.0;

    for (int i = 0; i < n; i++)
    {
        sum += arr[i];
    }

    return sum;
}

template <typename T>
T SumArray(T * arr[], int n)
{
    T sum = 0.0;
    for (int i = 0; i < n; i++)
    {
        sum += *(arr[i]);
    }

    return sum;
}


// main
int main(int argc, char **argv)
{
    p8_7();


    while (cin.get());
}
*/
//++++++++++++++++++++++++++++++++++7(wrong)++++++++++++++++++++++++++++++++++
/*
struct debts
{
   char name[50];
   double amount;
};
template<class T>
T sumarray(const T arr[],int );

int main()
{
   int things[6]={13,31,103,301,310,130};
   debts mr[3]=
   {
      {"ina",2400.0},
      {"hehe",1300.0},
      {"iby su",1800}
   };
   int a=sumarray(things,6);
   cout<<"numbers in things are "<<a<<endl;
   double b=sumarray(mr,3);
   cout<<"numbers in debts are "<<b<<endl;
   return 0;
}
template<class T>
T sumarray(const T arr[],int n)
{  T sum=0;
   for(int i=0;i<n;i++)
   {
      sum+=arr[i];
   }
return sum;
}
template<>
double sumarray(debts (&deb)[],int num)
{
   double sum;
   for(int i=0;i<num;i++)
   {
      sum+=deb[i].amount;
   }
   return sum;
}*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值