C++ 基础复习笔记

本文最后更新于:2023年3月31日 晚上

C++ 基础复习笔记

1、Hello World

1
2
3
4
5
6
7
#include <iostream>
using namespace std;

int main(void){
cout<< "Hello world!"<<endl;
return 0;
}

2、 别名

1
2
typedef double Area,Volume;
using Area = double; //只能为已有数据类型声明别名

3、 auto与decltype

1
2
auto i=0,j=1;
autp i=0,pi=3.14; //不行,输入数据只能同一类型

4、函数

1
2
3
4
5
6
7
8
9
10
double power(double x,int n){
double val = 1.0;
while(n--)
val*=x;
return val;
}

int power(int x,int y){
//函数重载
}

5、类与对象

类的定义
1
2
3
4
5
6
7
8
9
//类的定义
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
//类中有函数成员需要被频繁调用,代码比较简单的,可以直接在类里面定义(内联函数)
private:
int hour, minute, second;
};
函数成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//类的定义
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
//类中有函数成员需要被频繁调用,代码比较简单的,可以直接在类里面定义(内联函数)
}
private:
int hour, minute, second;
};

//函数成员
void Clock::setTime(int newH,int newM,int newS){
hour = newH;
minute = newM;
secodne = newS;
}

void Clock::showTime(){
cout<<"Hour:"<<hour<<"Minutes:"<<minute<<"Seconds"<<second<<endl;
}
内联函数的两种写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Clock{
public
void setTime(int newH,int newM,int newS);
void showTime();
void func(){
cout<<"这是内联函数的隐式写法"<<"当代码简单,用的次数比较多的时候可以用内联函数"
}
private:
int hour,minute,second;
}

inline void Clock::func(){
cout<<"这是内联函数的显式写法"
}
构造函数与析构函数

​ 作用:在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//类的定义
class Clock {
public:
//默认构造函数
Clock();
//构造函数
Clock(int newH,int newM,int newS);
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};

Clock::Clock(int newH,int newM,int newS):hour(new)
int mian(void){
Clock();//调用默认构造函数
Clock(0,0,0);//调用构造函数
}
委托构造函数

​ 构造函数 class_c(int, int, int) 首先调用构造函数 class_c(int, int),该构造函数反过来调用 class_c(int)。 每个构造函数将仅执行其他构造函数不会执行的工作。

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
38
39
40
41
42
43
class class_c {
public:
int max;
int min;
int middle;

class_c() {}
class_c(int my_max) {
max = my_max > 0 ? my_max : 10;
}
class_c(int my_max, int my_min) {
max = my_max > 0 ? my_max : 10;
min = my_min > 0 && my_min < max ? my_min : 1;
}
class_c(int my_max, int my_min, int my_middle) {
max = my_max > 0 ? my_max : 10;
min = my_min > 0 && my_min < max ? my_min : 1;
middle = my_middle < max && my_middle > min ? my_middle : 5;
}
};


class class_c {
public:
int max;
int min;
int middle;

class_c(int my_max) {
max = my_max > 0 ? my_max : 10;
}
class_c(int my_max, int my_min) : class_c(my_max) {
min = my_min > 0 && my_min < max ? my_min : 1;
}
class_c(int my_max, int my_min, int my_middle) : class_c (my_max, my_min){
middle = my_middle < max && my_middle > min ? my_middle : 5;
}
};
int main() {

class_c c1{ 1, 3, 2 };
}

复制构造函数

​ 作用:使用一个已经存在的对象,去初始化同类的一个新对象。把初始对象的每个数据成员的值都复制到新建立的对象中。在用户没有指定复制构造函数时,系统会自动生成复制构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point{
public:
Point(int xx=0,int yy=0){
x=xx;
y=yy;
}
//
Point (Point &p);
int getX(){return x;}
int getY(){return y;}
private:
int x,y;

}
Point::Point(Point &p){
x = p.x;
y = p.y;
cout<<"Calling the copy constructor"<<endl;
}
析构函数

​ 作用:用来完成对象被删除前的一些清理工作。在对象生命周期即将结束的时候被自动调用,不接受任何参数。

1
2
3
4
5
6
7
8
9
class Clock{
public:
Clock();
void setTime(int newH,int newM,int newS);
void showTime();
~Clock(){} //析构函数
private:
int hour,minute,second;
}
枚举类型与Union类型
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
/**
枚举类元素按常量处理,但不能对他们赋值,可以在声明时候另行定义值。
enum Weekday {MON,TUE,SAT,THU}
默认值依次为0,1,2,3;
enum Weekday {Sun=7,Mon=1,TUE,WED,THU}
**/

#include <iostream>

using namespace std;

enum GameResult {WIN,LOSE,TIE,CANCEL};

int main(void) {
GameResult result;
//指定 omit 为枚举类型 GameResult类中的CANCEL
enum GameResult omit = CANCEL;
for (int count = WIN; count <= CANCEL; count++) {
result = GameResult(count);
if (result == omit) {
cout << "The Game was cancelled" << endl;
}
else {
cout << "The game was played"<<endl;
if (result == WIN) {
cout << "Won" << endl;
}
else {
cout << "Lose" << endl;
}
}
}

return 0;
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>

using namespace std;

class ExamInfo {
public:
ExamInfo(string name,char grade);
ExamInfo(string name, bool pass);
ExamInfo(string name, int percent);
void show();

private:
string name;
//枚举类型 类型名未命名,在分号前声明变量
enum {
GRADE,
PASS,
PERCENTAGE
}mode;
//联合类型
union {
char grade;
bool pass;
int percent;
};
};

ExamInfo::ExamInfo(string name, char grade) :name(name), mode(GRADE), grade(grade) {};
ExamInfo::ExamInfo(string name, bool pass) :name(name), mode(PASS), pass(pass) {};
ExamInfo::ExamInfo(string name, int percent) :name(name), mode(PERCENTAGE), percent(percent) {};
void ExamInfo::show() {
switch (mode)
{
case ExamInfo::GRADE:
cout<<grade;
break;
case ExamInfo::PASS:
cout << pass ? 0 : 1;
break;
case ExamInfo::PERCENTAGE:
cout << percent;
break;
default:
break;
}
cout << endl;
}

int main(void) {
ExamInfo course1("CPU", 'N');
ExamInfo course2("GPU", 0);
ExamInfo course3("Tem", 80);
course1.show();
course2.show();
course3.show();
return 0;
}
综合案例
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include <cmath>

using namespace std;
class SavingAccount {
private:
int id;
double balance;
double rate;
int lastDate;
double accummulation;
void record(int date,double amount);
double accumulate(int date) const {
return accummulation + balance * (date - lastDate);
}
public:
SavingAccount(int date, int id, double rate);
int getID() { return id; }
double getBalance() { return balance; }
double getRate() { return rate; }
void deposit(int date, double amount);
void withdraw(int date, double amount);
void settle(int date);
void show();
};

SavingAccount::SavingAccount(int date, int id, double rate) :id(id), balance(0), rate(rate), lastDate(date), accummulation(0) {
cout << date << "\t#" << id << "is created" << endl;
}

void SavingAccount::record(int date, double amount) {
accummulation = accumulate(date);
lastDate = date;
amount = floor(amount * 100 + 0.5) / 100;
balance += amount;
cout << date << "\t#" << id << "\t" << amount << "\t" << balance << endl;
}

void SavingAccount::deposit(int date, double amount) {
record(date, amount);
}

void SavingAccount::withdraw(int date, double amount) {
if (amount > getBalance()) {
cout << "Error : not enough money" << endl;
}
else {
record(date, -amount);
}
}

void SavingAccount::settle(int date) {
double interest = accumulate(date) * rate / 365;
if (interest != 0) {
record(date, interest);
}
accummulation = 0;
}

void SavingAccount::show() {
cout << "#" << id << "\tBalance:" << balance<<endl;
}

int main(void) {
SavingAccount sa0(1, 21325302, 0.015);
SavingAccount sa1(1, 58320212, 0.015);
sa0.deposit(5, 5000);
sa1.deposit(25, 10000);
sa0.deposit(45, 5500);
sa1.withdraw(60, 4000);
sa0.settle(90);
sa1.settle(90);
sa0.show();
sa1.show();

return 0;
}
深度搜索
位域

​ 将类中的多个数据成员打包,从而使不同成员可以共享相同的字节的机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
用 : 后的位数来指定一个位域所占用的二进制位数
只有 bool,char,int,enum的成员才能被指定位域
**/

class student {
public:
student(unsigned number, Level level, Grade grade) :number(number), level(level), grade(grade) {};
void show();
private:
unsigned number : 27; //number 占用27个字节
Level level : 2; //level 占用两个字节
Grade grade : 2;//grade 占用两个字节
};

6、数据的共享与保护

作用域
类作用域
1
2
3
4
5
6
7
//类X中 m成员的类作用域

//没有声明同名的局部作用域标识符,在函数内可以直接访问成员m
x.m
x::m //访问类的静态成员

ptr->m //ptr为指向X类的一个对象的指针
enum作用域
1
2
3
4
5
6
enum color {red,yellow,green}; //不限定作用域 可以直接访问
enum class color2 {red,yellow,green}; //限定作用域 通过color2::访问

color c =red;
color c2 = color::red;
color2 c3 = color2::red;
生存周期
静态生存周期
1
2
3
4
5
//文件作用域 全局直接声明
//在局部作用域中以 static 声明

static int i = 5; //只赋值一次,下次使用时会保持上一次的值
static int j; //未指定初值的基本类型静态生存变量会被以0初始化
类的静态成员

​ 类属性:某个属性为整个类所共有,不属于某个具体对象,采用static声明

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>

using namespace std;

class Point {
public:
Point(int x = 0, int y = 0);
Point(Point& p);
~Point();
int getX();
int getY();
//静态函数成员
//主要用来访问同一个类中的静态数据成员
static void showCount();
private:
int x, y;
static int count; //静态数据成员 不指定初值,默认为0
//constexpr static int origin = 0;
};

Point::Point(int x , int y ) :x(x), y(y) {
count++;
};
Point::Point(Point& p) :x(p.x), y(p.y) {
count++;
};
Point::~Point() {
count--;
}

int Point::getX() {
return x;
}

int Point::getY() {
return y;
}

void Point::showCount() {
cout << "Object count=" << count << endl;
}

int Point::count = 0;
//constexpr int Point::origin;

int main() {
//静态函数调用
Point::showCount();
Point a(4, 5);
cout << "Point A " << a.getX() << "," << a.getY() << endl;
a.showCount();

Point b(a);
cout << "Point B " << b.getX() << "," << b.getY() << endl;
Point::showCount();
return 0;
}

​ 静态成员函数中访问类的非静态成员必须指明对象。

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
#include<iostream>
using namespace std;

class A {
public :
A();
static void f();
static void g(A a);
private:
int x;
static int y;
};

A::A() {
//构造函数,直接访问private x可以
cout << x;
};

void A::f() {
//静态成员函数中访问类的非静态成员必须指明对象。
cout << x; //错误,不能访问,非静态成员引用必须与特定对象相对
cout << y; //可以 能访问 静态成员函数可以直接访问静态成员
};

void A::g(A a) {
cout << a.x;
}
int main() {
return 0;
}
友元

​ 友元关系提供了不同类或对象的成员函数之间,类的成员函数与一般函数之间进行数据共享的机制。通过友元关系,一个普通函数或者类的成员函数可以访问封装于另一个类中的函数。

​ 友元关系单向、不能传递、不能继承。

友元函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Point{
public:
Point(int x=0,int y=0);
int getX();
int getY();
friend float dist(Point &p1,Point &p2);
private:
int x,y;
};

//dist 函数在Point类的外面,但是可以通过Point对象访问Point内部的数据
float dist(Point &p1,Point &p2){
//友元函数可以通过对象名直接访问Point类的私有数据成员
double x = p1.x-p2.x;
double y = p1.y-p2.y;
return static_cast<float>(sqrt(x*x+y*y));
}
友元类

AB的友元类,A类中所有成员都是B的友元函数,都可以访问B类的私有和保护成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A{
public:
void display();
friend class B;
private:
int x;
};

class B{
public:
void set(int i);
void display();
private:
A a;
};

void B::set(int i){
//B 是 A 的友元,在B的成员函数中可以访问A类对象的私有成员
a.x = i;
}
new和delete

​ 先分配memory,再调用ctor构造函数

1
2
3
4
5
6
7
Complex* pc = new Complex(1,)2;

Complex* pc;

void* men = operate new (sizeof(Complex)); //分配内存
pc = static_cast<Complex*>(mem);//转型
pc->Complex::Complex(1,2);//构造函数

​ 先调用dtor析构函数,再释放Memory

1
2
3
4
5
6
String* ps = new String("Hello");
...
delete ps;

String::~String(ps);
operator delete (ps);
const

​ 修饰常量、常对象。常对象不能被更新。

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
38
39
40
//常成员函数
//常成员函数调用期间,目的对象都被视为常对象,因此不能更新目的对象的数据成员。
//常对象只能调用常函数
类型说明符 函数名(参数表) const;

#include<iostream>
using namespace std;

class R {
public:
R(int r1, int r2);
//普通成员函数
void print();
//常成员函数
void print2() const;
private:
int r1, r2;
};

R::R(int r1, int r2) :r1(r1), r2(r2) {};
void R::print() {
cout << r1 << ":" << r2 << endl;
}

void R::print2() const {
cout << r1 << ";;" << r2 << endl;
}

int main() {
//普通对象 可以调用普通股成员函数、常成员函数
R a(5, 4);
a.print();
a.print2();
//常对象
//常对象只能调用常成员函数
const R b(20, 52);
//b.print(); 报错
b.print2();
return 0;
}
常数据成员与静态常数据成员
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
#include<iostream>
using namespace std;

class A {
public:
A(int i);
void print();
private:
const int a;
static const int b;
//静态常量如果具有整数类型后者枚举类型 可以直接定义
static const int b = 10;
};

//static const int b,
//b 定义了就不能改
const int A::b = 10;

A::A(int i) :a(i) {};

void A::print() {
cout << a << ":" << b << endl;
}

int main() {
A a1(100), a2(20);
//set(a1.b=100) 不行,定义了就不能改了
a1.print();
a2.print();
return 0;
}
常引用

​ 常引用的对象不能被更新。函数中无需修改两个传入对象的值,因此将传参方式改为传递常引用更合适。

​ 无需改变其值的参数,不宜使用普通引用方式加以传递。可以采用传值方式或者传递常引用方式。

​ 对于大对象来说,传值耗时较多,因此传递常引用为宜。

​ 复制构造函数的参数也宜采用常引用的方式。

1
2
3
4
5
6
7
class Point{
public:
...
friend float dist(const Point &p1,const Point &p2);
}

float dist(cosnt Point &p1,const Point &p2){};

总结

​ const 数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。所以不能在类的声明中初始化 const 数据成员,因为类的对象没被创建时,编译器不知道 const 数据成员的值是什么。

​ const 数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。

​ cosnt 成员函数主要目的是防止成员函数修改对象的内容。即 const 成员函数不能修改成员变量的值,但可以访问成员变量。当方法成员函数时,该函数只能是 const 成员函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test{
public:
Test();
enum {size1=100,size2=200};
private:
const int a;//只能在构造函数初始化列表中初始化
static int b;//在类的实现文件中定义并初始化
conststatic int c;//与 static const int c;相同。
};

Test::Test():a(0);
int Test::b=0;//static成员变量不能在构造函数初始化列表中初始化,因为它不属于某个对象。
const intTest::c=0;//注意:给静态成员变量赋值时,不需要加static修饰符,但要加const
const 与 static 变量定义很重要
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
//-----------------Test.h----------------------------
#pragma once
class Test
{
private :
int var1;
// int var11= 4; 错误的初始化方法
const int var2 ;
// const int var2 =22222; 错误的初始化方法
static int var3;
// static int var3=33333; 错误,只有静态常量int成员才能直接赋值来初始化
static const int var4=4444; //正确,静态常量成员可以直接初始化
static const int var44;
public:
Test(void);
~Test(void);
};
//--------------------Test.cpp-----------------------------------
#include ".\test.h"

int Test::var3 = 3333333; //静态成员的正确的初始化方法

// int Test::var1 = 11111;; 错误静态成员才能初始化
// int Test::var2 = 22222; 错误
// int Test::var44 = 44444; // 错误的方法,提示重定义
Test::Test(void):var1(11111),var2(22222)正确的初始化方法//var3(33333)不能在这里初始化
{
var1 =11111; //正确, 普通变量也可以在这里初始化
//var2 = 222222; 错误,因为常量不能赋值,只能在 "constructor initializer (构造函数的初始化列表)" 那里初始化

var3 =44444; //这个赋值是正确的,不过因为所有对象一个静态成员,所以会影响到其他的,这不能叫做初始化了吧
}
Test::~Test(void){}
mutable

mutalble使得常成员函数可以修改他们的值。被mutable修饰的成员对象任何时候都不会被视为常对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Line{
public:
Line(const Point &p1,const Point &p2):p1(p1),p2(p2),len(-1){};
double getLen() const;
private:
Point p1,p2;
mutable double len;
};
double Line::getLen() const{
if(len<0){
double x = p1.getX()-p2.getX();
double y = p1.getY()-p2.getY();
len = sqrt(x*x+y*y);
}
return len;
}
文件结构与外部引用

C++文件结构

1
2
3
.h 头文件 类定义文件 
.cpp 类实现文件
.cpp 类使用文件

外部引用

​ 一个变量除了在定义他的源文件中引用,还被其他文件引用。需要加extern文件关键字。

extern关键字声明的变量,如果同时指定类初值,则是定义性声明(声明的同时定义,分配内存初始化),否则是引用性声明。

通常情况下,变量和函数的定义都放在源文件中,而对外部变量和外部函数的引用性声明则放在头文件中。

1
2
3
4
5
// .cpp 1
int i = 3;

// .cpp2
extern int i ;

将变量和函数限制在编译单元内

1
2
3
4
5
6
7
8
//被namespace括起的区域都属于匿名的命名空间
//不会暴露给其他源文件
namespace{
int n;
void f(){
n++;
}
}
编译

预定义

1
2
#define
#undef

条件编译

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
// 形式1
#if ...
...
#endif

// 形式2
#if ...
...
#else
...
#endif

// 形式3
#if ...
...
#elif ...
...
#elif ...
...
#else ...
...
#endif

// 形式4
#ifdef ... //如果经过#define并且未经#undef删除
...
#else
...
#endif

// 形式5
#ifndef ...
...
#endif

defined操作符

defined是一个预处理操作符,而不是指令,因此不用#开头。

1
2
3
4
5
6
7
8
9
10
#ifndef MYHEAD_H
#define MYHEAD_H
...
#endif

//等价于
#if !defined(MYHEAD_H)
#define MYHEAD_H
...
#endif
堆栈内存管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Complex{...};
...

{
//c1所占空间来自Stack栈 存在于某一块作用域之内的空间
// 作用域结束生命结束 会被自动清理
Complex c1(1,2); //local object auto object

// p所占空间来自Heap堆 内存来自于全局内存,动态分配
Complex* p = new Complex(3);
...
delete p;
//static object 离开作用域生命仍然存在
static Complex c2(1,2);

}

C++ 基础复习笔记
https://anonymouslosty.ink/2023/03/31/C++ 基础复习笔记/
作者
Ling yi
发布于
2023年3月31日
更新于
2023年3月31日
许可协议