系列文章目录
文章目录
一、复合类型
C风格字符串
字符串用双引号表示,如 “S”为字符串,它有两个字符’S’和’\0’(\0自动添加,实际上就是0x00)。“S”实际上就是字符‘S’的地址
// 以下都是字符串
// '\0'是字符串结束的标志
const char* cstr = "abc"; // 'a', 'b', 'c', '\0'
char charr1[4] = "abc"; // 'a', 'b', 'c', '\0'
char ch[] = "abc"; // 'a', 'b', 'c', '\0'
char ch[10] = {'a', 'b', '\0'};
// 以下是字符数组
char ch[4] = {'a', 'b', 'c', 'd'};
cin
从输入缓冲区读取字符,默认以 空格、enter、tab结束
cin.getline(ch, 4)
读取一行(行尾为’\n’),然后清空输入缓冲区,将’\n’替换成’\0’
或者读取到第三个字符后+‘\0’停止读取,清空输入缓冲区中读取的字符
cin.get(ch, 4)
与getline相似,读取一行(以’\n’结尾)但是不将输入缓冲区中的’\n’清除,所以cin.get(xx,xx); cin.get(xxx,xx);会导致第二个get失效,一般cin.get(xx, xx).get();
cin.get() 可跳过换行符
混合输入字符串和数字
const int n = 10;
char arr[n];
char arr2[n];
int y;
cin >> y; // 添加cin.get()
// cin不会将输入缓冲区中的换行符清空
// getline看到换行符后认为输入结束
cin.getline(arr, n);
cout << y << arr;
c++ 风格字符串
用string
显示原始字符
cout << R"(Ji "King" "\n" instead of endl.)" << '\n';
// Ji "King" "\n" instead of endl.
// cout << R"任意字符("(yes'?)", sy.括号内为原始字符)同前" << '\n';
cout << R"+*(括号内为原始字符,不存在转义的情况)+*" << '\n';
类
类或者结构是对其实例化后对象的属性或方法的描述,因此一般不会在定义类或结构时对成员赋值。定义是描述性的语句,此时赋值是没有道理的。
在C++中列表初始化是一种较为通用的初始化方法(=是可选项,如果{}内没有东西则编译器会默认置零,不允许缩窄转换),可对结构体、普通变量等进行初始化。
结构体可在函数外部定义也可在函数内部定义。
可在结构中定义位字段
struct torgle_register
{
unsigned int SN : 4; // 4 bits for SN value
unsigned int : 4; // 4 bits unused
bool goodIn : 1; // valid input(1 bit)
bool goodTorgle : 1; // successful torgling
};
用new申请内存后一定要用delete释放掉,其它行为均为“未定义行为”
数组、指针
- 数组和指针基本等价,其根本差别是数组名是rvalue不能修改。
- 对数组名应用sizeof运算符得到的是数组的大小(元素类型字节x元素个数),对指针则是指针大小。
short tell[10]; // tell an array of 20 bytes
cout << tell; // displays &(tell[0])
cout << &tell; // displays address of whole array
从数字上说这两个值完全一样;但从概念上说,&tell[0](即tell)是一个2字节内存块的地址,而&tell是一个20字节内存块的地址。因此,表达式tell+1将地址值加2,而表达式&tell+1将地址值加20。换句话说tell的类型为(short*),而&tell的类型为short(*)[10]。即为
short (*pas)[10] = &tell; // pas points to array of 20 shorts
// pas相当于二级指针其类型为 short (*)[10]
// 两种表示基本等价
pw[1] *(pw + 1)
数组的动态联编和静态联编
int a[常量或常量表达式];
int size;
cin >> size;
int* arr = new int[size];
...
delete [] arr;
指针和字符串
char flower[10] = "rose;
cout << flower << "s are red\n";
- 在cout和C++表达式中,char数组名、char指针以及用引号括起来的字符串常量都被解释为字符串第一个字符的地址。
二、基本语句
C++表达式是值或值与运算符的组合,每个C++表达式都有值,且分为lvalue和rvalue
表达式后面加上分号即为一条语句。
// 对于以下语句,c++没有规定*两边表达式的运算顺序,所以不要这样做!
x = 2 * x++ * (3 - ++x);
循环与文本输入
通过键盘处输入的总是字符类型的数据,cin对象会根据要写入数据的类型自动将字符类型进行转换
char ch;
int count = 0;
cout << "input char end with \'#\'. \n";
cin >> ch;
while (ch != '#'){
cout << ch;
++count;
cin >> ch; // 修改为 cin.get(ch);
}
// compare
char str[100];
cin >> str;
cout << str;
output:
1:
start
wode shuru# commnet
wodeshuru
2:
inp: abd dfe df#con
out: abd
忽略空格、指标等字符
cin.get(ch) cin.get()
cctype
func name | discr |
---|---|
isalnum() | 是否为字母或数字 |
isalpha() | |
iscntrl() | 是否为控制字符 |
isdigit() | |
isgraph() | |
islower() | |
isprint() | |
ispunct() | |
isspace() | |
isuppper() | |
isxdigit() | 十六进制 |
tolower() | |
toupper() |
字符文件读写
#include <fstream>
ofstream outFile;
outFile.open(/* char* */);
outFile << xxx;
outFile.close();
ifstream inFile;
inFile.open(char*);
char line[100];
inFile.getline(line, 100);
if(!inFile.is_open()){
exit(EXIT_FAILURE);
}
引用
引用是变量的别名,引用与变量绑定,一旦绑定不可分割;引用必须初始化且只能使用左值初始化。
常量引用指不能通过该变量修改变量的值,因为是常量,所以可以引用左值和右值。
int a = 0, &refa = a; // 合法形式
double b = 3.1;
const int &refb = b; // 编译器将b转化为一个临时变量(右值),然后refb引用该临时变量
总结
C++数类型分为基本类型和复合类型。
基本类型分为整形和浮点型,复合类型如数组、指针、结构体等。
简单介绍了文件读写的相官操作。只靠考虑读写字符。
头文件#include <fstream>
写入文件为ofstream
读取文件为ifstream
15 异常
程序有时会遇到运行阶段错误,导致程序无法正常地运行下去。如,程序可能试图打开一个不可用的文件,请求过多的内存,或者遇到错误值。
abort
进程终止,或终止此进程并向父进程返回值
exit 刷新文件缓冲区,但不显示消息
___
throw 内容(对象或字符串都可);
class UserDefExcep: public std::exception
{
public:
virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW override
{
return "error";
}
};
int main()
{
try{
throw UserDefExcep(); // throw "error";
}catch(const std::exception& e /* const char*e */){
std::cerr << e.what() << '\n';
}// catch(...) {}
}
注: 引发异常时编译器总是创建一个临时拷贝,即使异常规范和catch中指定的是引用。
捕获所有异常
try{
xxx
}catch(...){
xxx
}
当new不出内存时返回std::bad_alloc异常
其它知识点
字符串字面值常量
在C++中,字符串字面值常量存储在全局区,必须用const char* 作为其指针,地址为首字母地址。
C++函数调用与返回
C++通常通过将信息放在栈中来处理函数调用。具体的,程序将调用函数的指令的地址(返回地址)放到栈中。当被调用的函数执行完毕后,程序将使用该地址来确定从哪里开始继续执行。另外,函数调用将函数参数放到栈中。在栈中,这些函数参数被视为自动变量。如果被调用的函数创建了新的自动变量,则这些变量也将被添加到栈中。如果被调用的函数调用了另一个函数,则后者的信息将被添加到栈中,以此类推。当函数结束时,程序流程将跳到该函数被调用时存储的地址处,同时栈顶元素被释放。因此,函数通常都返回到调用它的函数,以此类推,同时每个函数都在结束时释放其自动变量。如果自动变量是类对象,则类的析构函数(如果有的话)将被调用。
栈解退
现在假设函数由于出现异常(而不是由于返回)而终止,则程序也将释放栈中的内存,但不会在释放栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于try块中的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。这个过程被称为栈解退。引发机制的一个非常重要的特性是,和函数返回一样,对于栈中的自动类对象,类的析构函数将被调用。然而,函数返回仅仅处理该函数放在栈中的对象,而throw语句则处理try块和throw之间整个函数调用序列放在栈中的对象。如果没有栈解退这种特性,则引发异常后,对于中间函数调用放在栈中的自动类对象,其析构函数将不会被调用。
RTTI
typeid, type_info
RTTI只适用于包含虚函数的类。
STL
string
构造函数
ctor | descr |
---|---|
string(const char* s) | |
string(size_type n, char c) | n个c |
string(const string& str) | |
string() | default constructor |
string(const char* s, size_type n) | s的前n个字符 |
string(const string& str, size_type pos = 0, size_type n = npos) | 从位置pos开始的n个字符 |
template<class Iter> string(Iter begin, Iter end) | [begin, end) |
string(string&& str) noexcept | 移动构造函数 |
string(initializer_list<char> il) | 初始化列表 |
member functions
cin >> str; // read a word, ignore space
getline(cin, str); // read a line, discard \n
读取文件实力
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
int main()
{
using namespace std;
ifstream fin;
fin.open("abc.txt");
if (!fin.is_open()){
cerr << "Can't open file. Bye.\n";
exit(EXIT_FAILURE);
}
string item;
int count = 0;
// getline(fin, item, ':')
getline(fin, item);
do {
++count;
cout << count << ": "<< item << endl;
// getline(fin, item, ':')
getline(fin, item);
} while(fin);
cout << "Done\n";
fin.close();
return 0;
}
智能指针模板类
auto_ptr在C++98中使用,在C++11中已摒弃
#include <memory>
unique_ptr<X> px(new X);
shared_ptr<X> p2(new X);
- 不允许直接将指针赋值给智能指针类对象
- unique_ptr只能独享对象,若为右值(临时对象)赋给unique_ptr则编译器允许,其他情况的赋值则不被允许,即不允许留下悬挂指针
std::unique_ptr<string> ps1, ps2;
ps1 = std::unique_ptr<string>("abced");
ps2 = std::move(ps1); // allowed
ps1 = std::unique_ptr<string>("all right then.");
函数
for_each(first, last, fun), sort(first, last, comp), random_shuffle(begin, end)
并集:
set_union(setA.begin(), setA.end(), setB.begin(), setB.end(), insert_iterator<set<T>>(Bingji.begin()) );
交集:
set_intersection();
差:
set_difference();
set.insert(key); set.insert(A.begin(), A.end());
bool tooBig(int n) { return n > 100; }
list.remove_if(toBig);
<functional>
运算符 | 响应函数模板 |
---|---|
+ | plus |
- | minus |
* | multiplies |
/ | divides |
% | modulus |
- | negate |
== | equal_to |
!= | not_equal_to |
> | greater |
< | less |
>= | greater_equal |
<= | less_equal |
&& | logical_and |
|| | logical_or |
! | logical_not |
plus<\T>::result_type, plus<\T>::first_argument_type, plus<\T>::second_argument_type,
- vector, valarray, array
vector:
- 动态数组,实际数据存储在堆空间中
array:
- 类似数组,静态内存,提供了stl的结构,比数组更易用
valarray:
- 它支持按元素进行数学运算以及各种形式的广义下标运算符,切片和间接访问。
- 与向量相比,valarray在某些数学运算中比向量更有效。
initializer_list
#include <initializer_list>
double sum(std::initializer_list<double> il)
{
double tot = 0;
for (auto p = il.begin(); p != il.end(); p++){
tot += *p;
}
return tot;
}
sum({1, 2, 3, 4});
编程trick
两种状态之间转换
void onoff() { state ^= 1; } // state = 0 or 1 true or false