本文最后更新于: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
| string();
string(const string& rhs);
string(const char* s);
string(const string& rhs,unsigned int pos,unsigned int n);
string(const char*s ,unsigned int n);
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
| string append(const char* s);
string assign(const char* s);
int compare(const string& str) const;
string& insert(unsigned int p0,const char* s);
string substr(unsigned int pos,unsigned int n) const;
unsigned int fine(const basic_string &str) const;
unsigned int length() const;
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
| #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
|
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
| #include"date.h" #include<iostream> #include<cstdlib>
using namespace std;
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
| #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
|
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
| #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
| #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);
|
必须要用指针情况
- 所指向的对象,需要用分支语句确定或者在中途需要改变其所指向的对象。
- 指针的值可能是空指针,例如把指针作为函数的参数类型或者返回类型时,有时会用空指针表达特定的含义。
- 函数指针
- 使用
new动态创建的对象或数组,需要用指针来存储地址。
- 以数组形式传递大批量数据时,需要用指针类型作为参数接收。
指针的安全性
地址安全性
可能由指针的算数运算造成。一定要限制在用指向数组中某个元素的指针,得到指向同一个数组中另一个元素的指针。最典型的问题就是数组下标越界。
最安全的办法:尽量不直接通过指针来使用数组,而是使用封装类。
类型的安全性
基本数据类型之间的转换是基于内容的转换。
指针允许做类型的显示或者隐式转换。不同类型的指针的区别只是将相应地址中的数据被解释为不同类型而已。两种类型可能具有不同的空间
指针类型转换时安全性问题
堆对象的管理
new创建的对象,必须用delete删除