C++ 进阶笔记2

本文最后更新于:2023年4月7日 下午

C++ 进阶笔记2

字符串

​ C++ 中可以用字符型数组标准string类来存放字符串。

声明

一般声明

1
2
3
4
const char* str = "Program";
const char str[8] = {'P','r','o','g','r','a','m','\0'};
const char str[8] = "Program";
const char str[] = "Program";

string类

构造函数
1
2
3
4
5
6
7
8
9
10
11
12
//默认构造函数 创建一个长度为0的字符串
string();
//复制构造函数
string(const string& rhs);
//用指针s所指向的字符串常量初始化string类的对象
string(const char* s);
//将对象rhs中的串从位置pos开始取n个字符,用来初始化string类的对象
string(const string& rhs,unsigned int pos,unsigned int n);
//用指针s所指向字符串中的前n个字符初始化string类的对象
string(const char*s ,unsigned int n);
//将参数c中的字符重复n次,用来初始化string类中的对象
string(unsigned int n,char c)

注:string类具有接受const char* 类型的构造函数,因此,字符串常量字符数组表示的字符串变量都能隐含的转化为string对象

string str = "Hello world!";

操作符
string类操作符
常用成员函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//将字符串s添加在本串尾
string append(const char* s);
//赋值,将s所指向的字符串赋值给本对象
string assign(const char* s);
//比较本串与str串大小,本串小返回负数,相等返回0,大于返回正数
int compare(const string& str) const;
//将s所指的字符串位置插入本串中位置p0之前
string& insert(unsigned int p0,const char* s);
//去本串中位置pos开始的n个字符,构成新的string对象返回
string substr(unsigned int pos,unsigned int n) const;
//查找并返回str在本串中第一次出现的位置
unsigned int fine(const basic_string &str) const;
//返回字符串长度
unsigned int length() const;
//将本串与str中的字符串进行交换
void swap(string& str);
getline()函数
1
2
3
4
//接受输入的一整行
getline(cin,s2);
//输入字符串时候以","作为分隔符
getline(cin,s2,',');

综合案例

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
//date.h
#pragma once
#ifndef __DATE_H__
#define __DATE_H__

class Date {
private:
int year;
int month;
int day;
int totalDays;
public:
Date(int year, int month, int day);
int getYear() const { return year; }
int getMonth() const { return month; }
int getDay() const { return day; }
int getMaxDay() const;
bool isLeapYear() const {
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
void show() const;
int distance(const Date& date) const {
return totalDays - date.totalDays;
}
};

#endif // !__DATE_H__
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
//date.cpp
#include"date.h"
#include<iostream>
#include<cstdlib>

using namespace std;

namespace {
//namespace使得下面的定义只在当前文件中有效
const int DAYS_BEFORE_MONTH[] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };
}

Date::Date(int year, int month, int day) :year(year), month(month), day(day) {
if (day<0 || day>getMaxDay()) {
cout << "Invalid date:";
show();
cout << endl;
exit(1);
}
int years = year - 1;
totalDays = years * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
if (isLeapYear() && month > 2) {
totalDays++;
}
}

int Date::getMaxDay() const {
if (isLeapYear() && month == 2) {
return 29;
}
else {
return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
}
}

void Date::show() const{
cout << getYear() << "-" << getMonth() << "-" << getDay();
}
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
//account.h
#pragma once
#ifndef __ACCOUNT_H__
#define __ACCOUTN_H__

#include "date.h"
#include<string>


class SavingsAccount {

private:
std::string id;
double balance;
double rate;
Date lastDate;
double accumulation;
static double total;
void record(const Date& date, double amount, const std::string& desc);
void error(const std::string& msg) const;
double accumulate(const Date& date) const {
return accumulation + balance * date.distance(lastDate);
}

public:
SavingsAccount(const Date& date, const std::string& id, double rate);
const std::string& getId() const { return id; }
double getBalance() const { return balance; }
double getRate() const { return rate; }
static double getTotal() { return total; }
void deposit(const Date& date, double amount, const std::string& desc);
void withdraw(const Date& date, double amount, const std::string& desc);
void settle(const Date& date);
void show() const;
};

#endif // !__ACCOUNT_H__
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
//account.cpp
#include"account.h"
#include<cmath>
#include<iostream>

using namespace std;

double SavingsAccount::total = 0;

SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate)
:id(id), balance(0), rate(rate), lastDate(date), accumulation(0) {
date.show();
cout << "\t#" << id << " created" << endl;
}

void SavingsAccount::record(const Date& date, double amount, const string& desc) {
accumulation = accumulate(date);
lastDate = date;
amount = floor(amount * 100 + 0.5) / 100;
balance += amount;
total += amount;
date.show();
cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}

void SavingsAccount::error(const string& msg) const {
cout << "Error(#" << id << "):" << msg << endl;
}

void SavingsAccount::deposit(const Date& date, double amount, const string& desc) {
record(date, amount, desc);
}

void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) {
if (amount > getBalance()) {
error("no enough money");
}
else {
record(date, -amount, desc);
}
}

void SavingsAccount::settle(const Date& date) {
double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1));
if (interest != 0) {
record(date, interest, "interest");
}
accumulation = 0;
}
void SavingsAccount::show() const {
cout << id << "\tBalance:" << balance;
}
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
//main.cpp
#include "account.h"
#include<iostream>

using namespace std;

int main() {
Date date(2008, 11, 1);
SavingsAccount accounts[] = {
SavingsAccount(date,"11111",0.015),
SavingsAccount(date,"22222",0.015)
};
const int n = sizeof(accounts) / sizeof(SavingsAccount);
accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
accounts[1].deposit(Date(2008, 11, 25), 10000, "sell stock");

accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
accounts[1].withdraw(Date(2008, 12, 20), 4000, "by laptop");

cout << endl;
for (int i = 0; i < n; i++){
accounts[i].settle(Date(2009, 1, 1));
accounts[i].show();
cout << endl;
}
cout << "Total:" << SavingsAccount::getTotal() << endl;
return 0;
}

深度探索

指针和引用

​ 二者殊途同归,最后都是靠存储地址来实现的。除了语言形式上的差异之外,另一个显著差别就是普通指针可以多次赋值,多次更改其指向的对象,而引用只能在初始化的时候指定被引用的对象,其后就不能更改(引用声明后就已经具有常量性质)。

​ 指针可以再被取地址,但是引用本身的地址是不可以再被获取的。(引用一经定义,其全部行为全是针对被引用对象的,引用本身所占用的空间则完全被隐藏起来 )

指针常量与引用形式的对比

来源:为了能够更加方便、安全地处理数据双向传递,减少参数传递开销,C++对指针进行了简单的包装,引入了引用。

1
2
3
void Test(const Date& date);
//const : 不能用date引用来改变了date里的东西
//Date& 声明了这个引用就和传进来的date绑定了,不能换到别的引用。

必须要用指针情况

  1. 所指向的对象,需要用分支语句确定或者在中途需要改变其所指向的对象。
  2. 指针的值可能是空指针,例如把指针作为函数的参数类型或者返回类型时,有时会用空指针表达特定的含义。
  3. 函数指针
  4. 使用new动态创建的对象或数组,需要用指针来存储地址。
  5. 以数组形式传递大批量数据时,需要用指针类型作为参数接收。

指针的安全性

地址安全性

​ 可能由指针的算数运算造成。一定要限制在用指向数组中某个元素的指针,得到指向同一个数组中另一个元素的指针。最典型的问题就是数组下标越界

​ 最安全的办法:尽量不直接通过指针来使用数组,而是使用封装类。

类型的安全性

​ 基本数据类型之间的转换是基于内容的转换。

​ 指针允许做类型的显示或者隐式转换。不同类型的指针的区别只是将相应地址中的数据被解释为不同类型而已。两种类型可能具有不同的空间

指针类型转换时安全性问题

堆对象的管理

new创建的对象,必须用delete删除


C++ 进阶笔记2
https://anonymouslosty.ink/2023/04/03/C++ 进阶笔记2/
作者
Ling yi
发布于
2023年4月3日
更新于
2023年4月7日
许可协议