0%

C++重温

C艹.jpg

demo链接🔗https://github.com/samtake/C-review

快速入门

内联函数

C++提供了程序内联机制来减少函数调用的开销。在函数定义的返回值类型前面加一个inline关键字,“建议”编译器函数代码复制到程序中,避免函数调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using std::cout;
using std::cin;
using std::endl;


inline double cube(const double side){
return side*side*side;
}

void testInlineFunction(){
double sideValue;
for (int i=1 ; i<=3 ; i++) {
cout << "enter the side length of your cube:";
cin >>sideValue;

cout << "side=" << sideValue << "cube=" << cube(sideValue) <<endl;
}
}

  • using std::cout;using语句使我们不用重复写std::前缀;或者使用using namespace std;代替。

引用以及引用形参

  • 两种传参方式是:按值传递和按引用传递。
  • 采用按值传递方式传参时,首先生成实参值的副本,然后将该值传递给被调函数。改变副本的值不会影响主调函数中实参的值。
  • 通过按引用传参,允许被调函数直接访问主调函数中的数据,允许被调函数有选择的修改主调函数中的数据。
  • 引用实参是其对应的函数调用中实参的别名。
  • 通过&来标记按引用传参

引用以和引用形参

1
2
3
4
5
6
7
int squareByValue(int number){
return number *=number;
}

void squareByReference(int &numberRef){
numberRef *=numberRef;
}

函数体内引用作为别名

1
2
3
4
5
int z = 3;
int &z_alias = z;//int &z_alias;
cout << "z= " << z << endl << " z_alias = " << z_alias << endl;
z = 7;
cout << "z= " << z << endl << " z_alias = " << z_alias << endl;

输出

1
2
3
4
z= 3
z_alias = 3
z= 7
z_alias = 7

并且为初始化的引用int &z_alias;会导致错误

1
Declaration of reference variable 'z_alias' requires an initializer

空形参列表

在C++中,可以用void指定也可以通过在括号内不写任何语句定义空形参列表。如下两个函数是等价的:

1
2
void print();
void print(void);

默认实参

  • 有些程序在多次调用一个函数时经常将相同的实参传递给某个形参,这种情况下,可以将该形参指定默认实参,即传递给该形参的默认值。
  • 当发生函数调用时,如果省略了对应位置上的实参值,则编译器就会重写该函数调用并将默认值作为实参插入到函数调用中,在执行被调函数时,以该形参的默认值进行运算。
  • 默认实参必须是函数形参列表中最右边的实参。
  • 应该在函数名第一次出现时指定默认实参–通常在函数原型中指定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int boxVolume(int length = 1, int width = 1, int height = 1);

void testDefaultArgument(){
cout << "default = " <<boxVolume() << endl;

cout << "length = 2 : " <<boxVolume(2) << endl;

cout << "length = 2 , width =3 : " <<boxVolume(2,3) << endl;

cout << "length = 2 , width =3 , height = 4 : " <<boxVolume(2, 3, 4) << endl;
}


int boxVolume(int length, int width, int height){
return length*width*height;
}

一元作用域运算符

C++提供了一元作用域运算符,可以在含有与全局变量名的局部变量的域中访问该全局变量。

1
2
3
4
5
6
7
8
9
int number = 7;
void testUnaryOperation(){
double number = 10.7;

cout << "location number = " << number <<endl;

cout << "gloable number = " << ::number <<endl;

}

输出

1
2
location number = 10.7
gloable number = 7

函数重载

  • C++中允许定义同名的函数,只要这些函数具有不同的形参集合即可(至少在形参类型或形参个数或形参类型的顺序上有区别)。这个功能称为函数重载。
  • 调用重载函数时,C++编译器通过检查函数调用语句中的实参个数、类型及顺序选择适当的函数。
  • 重载函数由函数的签名进行区分。
  • 编译器根据函数形参的个数与类型将每个函数标识符编码,以保证类型安全链接,从而确保可以调用适当的重载函数,也确保实参与形参类型一致。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void functionOverloading(){
    cout << square(7) <<endl;
    cout << square(0.5) <<endl;
    }

    int square(int x){
    cout << "integer : ";
    return x*x;
    }

    double square(double x){
    cout << "double : ";
    return x*x;
    }
    输出
    1
    2
    integer : 49
    double : 0.25

函数模版

重载函数用于对不同数据类型的程序逻辑执行相似操作。如果各种数据类型的程序逻辑和操作是完全相同的,那么使用函数模版可以更加简洁、方便的执行重载。

  • 所有函数模版定义均以关键字template开始,随后是用尖括号<>括起来的模版形参列表.
  • 模版列表中的每个形参(称为形式类型形参)前面都加关键字typenameclass。两者含义相同。
  • 形式类型形参是基本数据类型或用户定义类型的占位符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <class T>
    T maximum(T value1, T value2, T value3 ) {
    T maximumValue = value1;

    if (value2 > maximumValue) {
    maximumValue = value2 ;
    }

    if (value3 > maximumValue) {
    maximumValue = value3 ;
    }

    return maximumValue;
    }

类与对象

在C++中通常有main函数以及一个或多个既包含数据成员也包含成员函数的类构成,所以:

  • 在一个类中,可提供一个或多个成员函数。
  • 类似的,对象也有属性,在程序中使用对象时带有这些属性。这些属性是对象所属的类的一部分。

定义一个具有成员函数的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//定义
class GradeBook {
//
public:
void displayMessage(){
cout<<"grade book"<<endl;
}
};

//调用
void funcTestGradeBook(){
GradeBook myGradeBook;
myGradeBook.displayMessage();
}

输出

1
2
3
Hello, World!
grade book
Program ended with exit code: 0
  • 类的定义由 class及随后的类名GradeBook开始,类名通常使用驼峰模式拼写,通常首字母大写。
  • 每个类体由左右打括号{} 括起来。
  • 类定义由;分号结束。
  • 成员函数的访问限定符有:publicprivateprotected三种。
  • 在定义函数时,必须指定返回值类型。
  • 成员函数名后的空括号表示该成员函数不需要传参。

定义一个有参成员函数

1
2
3
4
//定义一个有参成员函数
void displayMessageWithParms(string courseName){
cout<<"grade book"<< courseName <<endl;
}

调用

1
2
3
4
5
6
string courseName;
cout<< "enter course name:"<<endl;
getline(cin, courseName);
cout<<endl;
myGradeBook.displayMessageWithParms("Chinese");
myGradeBook.displayMessageWithParms(courseName);

输出

1
2
3
4
5
6
enter course name:
kjjkjkj

grade bookChinese
grade bookkjjkjkj
Program ended with exit code: 0

数据成员、set函数与get函数

  • 在函数定义体重声明的变量称为局部变量,它们只能在从声明它们的行到该函数定义的结束右打括号}的范围内使用。
  • 类通常包含一个或多个成员函数来操作属于该类特定对象的属性。属性在类定义中表示为变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class GradeBookHaveLocalVariable{
public:
void setCourseName(string name){
courseName = name;
}
string getCourseName(){
return courseName;
}

void displayMessage(){
cout<<"course name: "<< getCourseName() <<endl;
}
//定义一个有参成员函数
void displayMessageWithParms(string courseName){
cout<<"grade book"<< courseName <<endl;
}
private:
string courseName;
};

输出

1
2
course  name: 语文
Program ended with exit code: 0

用构造函数初始化对象

构造函数:

  • 用于在创建该类对象时初始化该对象。
  • 是一个特殊的成员函数,它的名字必须与类名相同。
  • 不能有返回值。
  • 通常声明为public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class GradeBookConstructor{

public:

GradeBookConstructor(string name){
setCourseName(name);
}

void setCourseName(string name){
courseName = name;
}

string getCourseName(){
return courseName;
}

void displayMessage(){
cout<<"course name: "<< getCourseName() <<endl;
}

//定义一个有参成员函数
void displayMessageWithParms(string courseName){
cout<<"grade book"<< courseName <<endl;
}

private:
string courseName;
};


void funcTestGradeBookConstructor(){
GradeBookConstructor myGradeBookConstructor1("Constructor");
GradeBookConstructor myGradeBookConstructor2("构造函数");

myGradeBookConstructor1.displayMessage();
myGradeBookConstructor2.displayMessage();
}

输出

1
2
3
course  name: Constructor
course name: 构造函数
Program ended with exit code: 0

用set函数验证数据

我们在.h文件中进行类的定义,并包含指定指定类的接口(类的public成员函数)的函数原形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VertifyWithSetFunc {
//instance variables

public:

VertifyWithSetFunc(string);

void setCourseName(string);
string getCourseName();

void displayMessage();

private:
string courseName;
};

在单独的源码文件中定义成员函数:每个成员函数名前面都有类名与二院作用域运算符::,这将每个成员函数绑定到声明成员函数与数据成员的VertifyWithSetFunc类定义上。

1
2
3
VertifyWithSetFunc::VertifyWithSetFunc(string name){
setCourseName(name);
}

我们知道可以通过set函数,可以修改private数据成员的值。这里我们要做的是在set函数进行值得判断。

1
2
3
4
5
6
7
8
9
10
void VertifyWithSetFunc::setCourseName(string name){
if (name.length() <= 10) {
courseName = name ;
}

if (name.length() > 10) {
courseName = name.substr(0, 10);
cout<< "name is long than 10 :"<< name << endl;
}
}

输出

1
2
3
name is long than 10 :test2test2test2test2test2test2test2test2test2test2
vertify1: test1
vertify2: test2test2

访问成员函数的三种方式

访问成员函数的三种方式:

对象名

1
2
3
4
Count counter;
counter.setX(1);
cout << "counter.setX(1);" ;
counter.print();

指向对象的指针

1
2
3
4
Count *counterPtr = &counter;
counterPtr->setX(2);
cout << "counterPtr->setX(2);";
counterPtr->print();

对象的引用

1
2
3
4
Count &counterRef = counter;
counterRef.setX(3);
cout << "counterRef.setX(3);";
counterRef.print();

输出

1
2
3
4
counter.setX(1);1
counterPtr->setX(2);2
counterRef.setX(3);3
Program ended with exit code: 0

析构函数

析构函数(destructor)是一种特殊的成员函数。

  • 类的析构函数名是在类名前加一个波浪好~
  • 累的析构函数是在删除对象的时候被隐式调用
  • 析构函数没有形参页不反悔任何值
  • 析构函数必须是 public类型的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CreateAndDestructor::CreateAndDestructor(int ID, string msg){
objectID = ID;
message = msg;

cout << "Object " << objectID << " 构造函数 runs " <<message << endl;
}


void create(void){
cout << "createFunc " << endl;
CreateAndDestructor fifth(5, "(local automatic in create)");
static CreateAndDestructor sixth(6, "(local automatic in create)");
CreateAndDestructor seventh(7, "(local automatic in create)");
}

CreateAndDestructor::~CreateAndDestructor(){
cout<<(objectID ==1 || objectID == 6 ? "\n" : "");

cout << "Object " << objectID << " 析构函数 runs " <<message << endl;
}


void testCreateAndDestructor(){
cout << "testFunc " << endl;
CreateAndDestructor second(2, "(local automatic in testFunc)");
static CreateAndDestructor third(3, "(local automatic in testFunc)");

create();


cout << "testFunc : " << endl;

CreateAndDestructor forth(4, "(local automatic in testFunc)");

cout << "\ntestFunc end \n" << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
testFunc 
Object 2 构造函数 runs (local automatic in testFunc)
Object 3 构造函数 runs (local automatic in testFunc)
createFunc
Object 5 构造函数 runs (local automatic in create)
Object 6 构造函数 runs (local automatic in create)
Object 7 构造函数 runs (local automatic in create)
Object 7 析构函数 runs (local automatic in create)
Object 5 析构函数 runs (local automatic in create)
testFunc :
Object 4 构造函数 runs (local automatic in testFunc)

testFunc end

Object 4 析构函数 runs (local automatic in testFunc)
Object 2 析构函数 runs (local automatic in testFunc)

Object 6 析构函数 runs (local automatic in create)
Object 3 析构函数 runs (local automatic in testFunc)
Program ended with exit code: 0
  • 在全局作用域内定义的对象的构造函数在该文件中的任何其他函数(包括main函数)开始执行之前执行。
  • main函数终止时,调用响应的析构函数。exit函数强制程序立即终止并且不执行自动对象的析构函数。
  • 自动局部对象的 析构函数在执行到达定义该对象的程序点是调用,对应的析构函数是在该对象离开该对像所在的作用域时调用。
  • static局部对象的析构函数只在执行第一次到达定义对象的程序点时调用一次,对应的析构函数是在main函数终止时或者exit函数时调用。