C++助教篇5_Week3不完全知识点总结

Posted xioacd99

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++助教篇5_Week3不完全知识点总结相关的知识,希望对你有一定的参考价值。

cout格式化输出

利用setf/unsetf or flag来设置cout的格式,前者是在当前状态下追加or删除指定格式,后者是重新设置一个新的格式。如

cout.setf(ios::right);  //右对齐

下面从常见格式、对齐与占宽、进制与+/-号、浮点数格式设置来介绍cout格式化输出

常见格式

ios::dec		以10进制表示整数
ios::hex		以16进制表示整数
ios::oct		以8进制表示整数
ios::showbase	为整数添加一个表示其进制的前缀
ios::internal	在符号位和数值的中间插入需要数量的填充字符以使串两端对齐
ios::left		在串的末尾插入填充字符以使串居左对齐
ios::right		在串的前面插入填充字符以使串居右对齐
ios::boolalpha	将bool类型的值以true或flase表示,而不是1或0
ios::fixed		将符点数按照普通定点格式处理(非科学计数法)
ios::scientific	将符点数按照科学计数法处理(带指数域)
ios::showpoint	在浮点数表示的小数中强制插入小数点(默认情况是浮点数表示的整数不显示小数点)
ios::showpos	强制在正数前添加+号
ios::skipws		忽略前导的空格(主要用于输入流,如cin)
ios::unitbuf	在插入(每次输出)操作后清空缓存
ios::uppercase	强制大写字母

setf有两种形式

  1. 一个参数的,如cout.setf(ios::right);可以设置为指定的格式(设置为右对齐)
  2. 两个参数的,如cout.setf(ios::right,ios::adjustfield);可以取消原来的一种格式,设置一种新的格式(取消其他对齐,设置为右对齐)
  3. 同属一个参数的,但是可以设置多种格式,需要通过|来组合格式,如cout.setf(ios::right | ios::hex);(设置为16进制右对齐)

但是,通过格式控制输出不能满足多种格式的要求,在这种情况下,我们可以使用STL提供的<iomanio>来实现。<iomanip>提供了如setioflagssetbasesetfillsetwsetprecision来方便控制格式

下面通过代码一个个说明其用途(请运行代码查看效果)

对齐与占宽

// endl是flush buffer的newline,'\\n' not ...

cout.flags(ios::left);  // ios::left左对齐,设置格式
cout << setw(20) << -123.456 << "hello" << endl;  // setw()是设置宽度
// equal to cout << left << setw(20) << -123.456 << "hello" << endl;
// 一次性,更方便

cout.flags(ios::internal);  // ios::interal两端对齐
cout << setw(20) << -123.456 << "hello" << endl;
// equal to cout << interal << setw(20) << -123.456 << "hello" << endl;

cout.flags(ios::right);  // ios::right右对齐
cout << setfill('*') << setw(20) << -123.456 << "hello" << endl;
// setw()默认使用空格填充,可以通过setfill()来自定义填充字符,一旦更改,除非重新设置,否则保持不变
// equal to cout << right << setw(20) << -123.456 << "hello" << endl;

进制与+-

cout.setf(ios::showpos | ios::uppercase);  //整数显示+,进制有字母的大写

cout.setf(ios::hex);  // 16进制的格式输出
cout << 123456 << endl;
// equal to cout << hex << 123456 << endl;

cout.setf(ios::dec);  // 10进制的格式输出
cout << 123456 << endl;
// equal to cout << dec << 123456 << endl;

cout.setf(ios::oct);  // 8 进制的格式输出
cout << 123456 << endl;
// equal to cout << oct << 123456 << endl;

cout.setf(ios::showbase);  //显示进制的前缀
cout << hex << 123456 << endl;
cout << dec << 123456 << endl;
cout << oct << 123456 << endl;
// equal to cout << showbase << hex << 123456 << dec << 123456 << oct << 12346 << endl;

cout.unsetf(ios::showbase);  //取消显示进制的前缀
cout << hex << 123456 << endl;
cout << dec << 123456 << endl;
cout << oct << 123456 << endl;
// equal to cout << noshowbase << hex << 123456 << dec << 123456 << oct << 12346 << endl;

cout.unsetf(ios::showpos | ios::uppercase);  //取消前面设置的格式

浮点数格式设置

cout.setf(ios::fixed);  //四舍五入非科学表示法
cout << setprecision(2) << 123.456 << endl;
// equal to cout << fixed << setprecision(2) << 123.456 << endl;

cout.setf(ios::scientific | ios::floatfield);  //四舍五入科学表示法
cout << setprecision(2) << 123.456 << endl;
// equal to cout << scientific << setprecision(2) << 123.456 << endl;

但是,C/C++的格式化输出还是使用printf更方便,代码也更简洁易读

const

const的应用(共享数据保护的一种方式)

const修饰的数据类型是指常类型,常类型的变量或对象的值不能被更新。即const限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。

  1. 常数据成员变量

    常数据成员变量允许在构造函数中初始化,但不能在函数体内初始化,只能在初始化列表中初始化。

  2. 常成员函数

  • 声明:<类型标识符>函数名(形参列表)const;

  • 说明:

    • const是函数类型的一部分,在实现部分也要带上该关键字;
    • const关键字可以用于对重载函数的区分;
    • 常成员函数不能修改类的成员变量,也不可以调用类中没有用const修饰的成员函数,只能调用常成员函数,但是可以被其他成员函数调用;
    • 特别地:常对象只能访问类中const成员函数(除了系统自动调用的隐含构造函数以及析构函数)
  1. 常引用:被引用的对象不能被更新。如果想要使用类的私有成员,又想保证私有成员值的安全性(即私有成员的数值不被改变)的时候,可以使用常引用。

    const 类型标识符 &引用名;

  2. 常对象:用const修饰的对象。

    const 类名 对象名;

    常对象只能调用常成员函数,必须进行初始化。

  3. 临时变量不能作为非const的引用参数

const的作用

  1. 欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
  2. 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
  3. 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
  4. 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
  5. 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
  6. 函数参数的const是告诉函数体不允许修改参数
  7. 函数末尾的const是告诉函数体不允许修改本类的数据成员

其他需要注意的地方

  1. 如果不想让编译器察觉到对 const 的操作,可以在 const 前面加上 volatile 关键字。Volatile 关键字跟 const 对应相反,是易变的,容易改变的意思。所以不会被编译器优化,编译器也就不会改变对 a 变量的操作。

  2. 当有个成员函数想修改对象中的某一个成员时,这时我们可以使用 mutable 关键字修饰这个成员,mutable 的意思也是易变的,容易改变的意思,被 mutable 关键字修饰的成员可以处于不断变化中。

  3. C++ const引用、临时变量与引用参数:如果实参与引用参数不匹配,C++将生成临时变量。如果引用参数是const,则编译器在下面两种情况下生成临时变量:

    • 实参类型是正确的,但不是左值

    • 实参类型不正确,但可以转换为正确的类型

    左值参数是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和被解除引用的指针都是左值,非左值包括字面常量和包含多项式的表达式

  4. 从c++中临时变量不能作为非const的引用参数:解释不是临时变量是常量不允许change,而是C++的语义限制。

    下面的代码中临时变量(a+b)的val被赋值成了c的val(请运行代码查看)

#include <iostream>
using namespace std;

class TestClass 
    friend TestClass operator+(const TestClass &left, const TestClass &right);
    friend ostream &operator<<(ostream &os, const TestClass &obj);

public:
    TestClass() = default;
    TestClass(int x) : val(x) 

private:
    int val;
;

TestClass operator+(const TestClass &left, const TestClass &right) 
    return TestClass(left.val + right.val);


ostream &operator<<(ostream &os, const TestClass &obj) 
    os << obj.val;
    return os;


int main(int argc, const char **argv) 
    TestClass a(2), b(3), c(4);
    cout << (a + b) << endl;
    cout << ((a + b) = c) << endl;  //临时对象作为左值
    system("pause");
    return 0;
                                                                       

应尽可能使用const

  • 使用cosnt可以避免无意总修改数据的编程错误

  • 使用const使函数能够处理const和非const实参,否则将只能接受非const数据

  • 使用const引用使函数能够正确生成并使用临时变量

类作为函数的返回值

当类中成员函数的参数不止一个,且需要同时作为返回值返回时,可用类的形式返回,且在定义返回值类型的时候,返回值类型名应与类名相同

友元函数

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。A是B的友元不代表B是A的友元。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend 进行声明

因为友元函数没有this指针,则参数要有三种情况:

  1. 要访问非static成员时,需要对象做参数。

  2. 要访问static成员或全局变量时,则不需要对象做参数。

  3. 如果做参数的对象是全局对象,则不需要对象做参数.可以直接调用友元函数,不需要通过对象或指针。

注意:友元是C++提供的一种破坏数据封装和数据隐藏的机制,为了确保数据的完整性,每次使用友元函数时最好检查一下友元函数是否会改变private或protected内变量的值。

运算符重载

运算符重载是一种形式的C++多态。其格式operator符号(参数)。例如,operator+()重载为运算符"+“的重载,operator*()为运算符”*“的重载。op必须是有效的C++运算符,不能虚构一个新的符号。一般而言,四则运算的重载函数返回类型仍为给定类(如复数类加法重载运算,返回类型仍是复数类,所以在main函数中可以实现z1=z2+z3的操作),”==""!="返回int。

需要注意的是:C++规定:“=”、“[ ]”、“( )”、“->” 这四个运算符只能被重载为类的非静态成员函数,而不能被重载为友元函数,这是需要特殊记忆的。

运算符重载是对已有的运算符赋予多重含义。C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)。

定义一个重载运算符的函数,在需要执行被重载的运算符时,把指定的运算表达式转化为对运算符函数的调用(系统自动调用)。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。编译系统对重载运算符的选择,遵循函数重载的选择原则(根据实参的类型来确定需要调用的函数)。

重载运算符的规则如下:

  1. C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。

  2. C++并非所有运算符都能重载,有一些运算符不能重载。(已于下表给出)

  3. 重载不能改变运算符运算对象的个数。

  4. 重载不能改变运算符的优先级和结合性。

  5. 重载运算符的函数不能有默认参数。

  6. 重载的运算符必须和用户定义的自定义类型的对象一起使用,至少有一个应为类对象。

注:用成员方式重载二元运算符时, 只需要一个右操作参数, 另一个左操作参数由this指针传入,所以,像TestClass::operator+(TestClass&, TestClass&)都要改成TestClass::operator+(TestClass&)

第一个参数默认由左操作数的this指针自动传入到函数中去的。因此,如果左操作数不是自定义的类的对象,而是int型等其他类型时,不可用成员方式重载,而应用友元函数重载,这样左操作数的类型就没有限制了。下面是可重载的运算符列表:

双目算术运算符+ (加),-(减),*(乘),/(除),% (取模)
关系运算符==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
逻辑运算符||(逻辑或),&&(逻辑与),!(逻辑非)
单目运算符+ (正),-(负),*(指针),&(取地址)
自增自减运算符++(自增),–(自减)
位运算符| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放new, delete, new[ ] , delete[]
其他运算符()(函数调用),->(成员访问),,(逗号),[](下标)

下面是不可重载的运算符列表:

  • .:成员访问运算符
  • .->:成员指针访问运算符
  • :::域运算符
  • sizeof:长度运算符
  • ?:条件运算符
  • #: 预处理符号

(单目运算符是只对一个变量进行操作的运算符,双目运算符是对两个变量进行操作的运算符…)

流运算符重载

可以对<<>>运算符进行重载。

cout是一个ostream对象,能够识别所有的C++基本类型,这是因为对于每种基本类型,ostream类声明中都包含了相应的重载的operator<<()定义。所以,同样的,它也可以对自己规定的类型进行定义。如

ostream& TestClass::operator<<(ostream& os, const TestClass& obj) 
istream& TestClass::operator>>(istream& in, TestClass& obj) 

以上是关于C++助教篇5_Week3不完全知识点总结的主要内容,如果未能解决你的问题,请参考以下文章

C++助教篇7_继承的十种常见情况

C++助教篇9_Week4找bugs

C++助教篇8_操作符重载的前后置

川师2016上半年软件工程助教总结

神经网络- 吴恩达Andrew Ng CNN卷积神经网络 Object Detection Week3 知识总结

2018年上半年软件测试助教小结