C++随记
- int main() 后的return 0 不写默认隐含
- C++语序赋值语句a = b = c = d = 1;从右向左执行
- 函数库double x = sqrt(6.25); //x = 2.5
- pow()计算幂的函数
- rand()返回随机数
- 函数调用必须包含括号,即使没有参数
- 函数命名:小驼峰式myFunction
- 变量名:my_onions
- sizeof运算符:int a; 大小为:sizeof(int)或sizeof a;
- 头文件:INT_MAX定义int最大值:#define INT_MAX 32767
- c++11变量初始化:int a{5}; int a = {5};int a{}; int a = {};//后者为初始化0
- 无符号类型:unsigned = unsigned int
- 十进制:42 八进制:033 十六进制:0xA1
- 修改显示方式:cout<<hex; cout<<oct;
- 打印单个字符:cout.put(‘a’);
- bool类型:任何非0值均为true(1) 否则为false(0)
- 创建常量:const type name = value; 等价于 #define name value
- 固定定点表示:cout.setf(ios_base::fixed,ios_base::floatfield);且不会删除末尾0
- 类型转换:值将被转换为接收变量的类型
- 强制类型转换:不会修改本身,而是创建一个新的变量:格式:(typename) value;或typename (value);
- static_cast (value); 将值从一种类型转换为另一种类型的方式 如static_cast (ch);
- 关键字auto将把变量类型设置为与初始值相同
- 数组声明:typeName arrayName[arraySize]; sizeof(arrayName); sizeof(arrayName[0]);
short array[] = {1,2,3,4};
int num_elements = sizeof array / sizeof (short);
- c++初始化数组:
double array[4] {1,2,33,4};
int array[4] {}; //初始化为0
char fish[] = "Bubbles";
- 数组中使用字符串:头文件 strlen()获取字符串长度
- c++字符串按行输入:cin.getline(name,size); 第一个参数为字符串数组名,第二个参数为大小 当遇到换行或大小为size时停止输入 每次读取一行 不保存换行符
char str[15];
cin.getline(str,15); //最大14字符 含空格
-
cin.get(str,15);会将最后的换行符保存在读取对列中 cin.get()读取下一个字符
-
拼接使用:
cin.get(name,arraySize).get(); //等同于一次cin.getline(name,arraySize);
cin.getline(name1,arraySize1).getline(name2,arraySize2); //等同于两次getline调用
-
get()读取空行后会设置失效位置,不再输入
-
当输入过长时,getline()和get()会将剩下的字符放在输入队列,但getline()会设置失效位
-
设置失效位,输入阻断,可用cin.clear();恢复
-
int year; (cin>>year).get(); //吸收换行符
-
字符串初始化:
string str1 = "aaa"; string str2{"saa"}; string str3 = {"aaa"}; stirng str4; str4 += str1; string str5 = str1; string str6 = str1 + str2;
-
获取string字符串大小:int a = str.size();
-
读取一行到字符串str:getline(cin,str);
-
结构体:
struct inflatable{ int a; float b; double c; };
-
在声明结构体变量时可省略关键字struct ,如:inflatable in; in.c = 1;
-
结构体数组
-
指针int* ptr;
-
int* p1,p2; 这里p1是指针 p2是整型量
-
为一个int值分配未命名的内存并用指针访问:int* pn = new int;
-
为一个数据对象获得并指定分配内存的通用格式:
typeName* pointer_name = new typeName;
-
new出的对象被分配在堆中或自由存储区中
-
配对new使用delete delete pointer_name;
-
一般不要创建指向同一块内存的指针,或许会释放内存两次
-
new创建动态数组:type_name* pointer_name = new type_name [num_elements];
int* psome = new int [10]; //new返回第一个元素的地址 delete [] psome; //释放
-
利用new读取大量字符串节省空间:
char* getName(){ char temp[80]; cout<<"Enter last name:"; cin>>temp; char* pn = new char[strlen(temp)+1]; strcpy(pn,temp); return pn; } int main(){ char* name; name = getName(); cout<<name<<"at"<<(int*)name<<endl; delete [] name; }
-
vector类模板::vector vt(n_elem);
vector<int> vi; vector<int> vt(n); //n可以是常量或变量
-
array模板类::array<typeName,n_elem> arr;
array<int ,5> ai; //n只能为常量
-
for循环:for(int i = limit ; i ; i–) quits when i is 0
-
显示bool型:cout.setf(ios_base::boolalpha);
-
for循环反向输出字符串
string str; cin>>str; for(int i = str.size(); i >= 0; --i) cout<<str[i];
-
基于范围的for循环c++11
double prices[5] = {1,2,3,4,5.0}; for(double x : prices) cout<<x<<" "; for(double &x : prices) x = x * 0.8; for(int x : {1,2,3,4,5,6,7,8}) cout<<x<<" ";
-
EOF检测:(事后)
char ch; cin.get(ch); while(cin.fail()==false){ cout<<ch; cin.get(ch); }
-
EOF后将停止输入,可用cin.clear()清除EOF
-
while(cin.fail()==false)等价于while(!cin.fail())
-
以下代码等价于54条:
char ch; while(cin.get(ch)) cout<<ch;
-
while((ch = cin.get()) != EOF)等价于while(cin.fail()==false)
-
cin.get(ch)等价于ch = cin.get()
-
二维数组:
const char* cities[5] = { "1121","0000","10","daw","222" }; const string cities[5] = { "1121","0000","10","daw","222" };
-
字符库函数: if(isalpha(ch)) //如果ch是字母 if(ispunct(ch)) //如果ch是符号 if(isdigits(ch)) //如果ch是数字 if(isspace(ch)) //如果ch是空格
-
三目运算符:expression1 ? expression2 : expression3
-
while(i<Max && cin >> fish[i]) //当读取不是数字时程序停止
-
使用cin输入表达式的值检测是否非法输入: 采取的步骤: 重置cin以接收新的输入 删除错误输入 提示用户再输入 int golf[5]; for(int i = 0;i<5;++i){ cout<<"round #"<<i+1<<":"; while(!(cin>>golf[i])){ //错误处理 cin.clear(); while(cin.get()!= '\n') //删除行尾之前的所有输入 很重要!! continue; cout<<"Please enter a number:"; } }
-
写入到文本文件:
ofstream ofs; ofs.open("filename.txt"); string str; ofs.open(str); ofs<<8888; ofs<<str; ofs.close();
-
cout<<fixed ;用一般方式输出浮点型 cout.precision(2) ;设置精度为2 cout.setf(ios_base::showpoint) ;显示浮点数后面的0
-
ofs.open(文件) 若存在则覆盖 不存在则新建文件
-
读取到文本文件:
ifstream ifs; ifs.open("file.txt"); if(!ifs.is_open()){ exit(EXIT_FAILURE); //<cstdlib> } ifs.getline(line,80); ifs.close();
-
读取文件的几点检查:
//1. 文档末尾 //2. 类型不匹配 //3. 意外故障 while(ifs.good()){ //while input good and not at EOF } //分类检查: if(ifs.eof()) cout<<"End of file reached"; else if(ifs.fail()) cout<<"Input terminated by data mismatch"; else cout<<"Input terminated for unknowed reason";
-
循环读取的执行
ifs >> value; while(ifs.good()){ //loop body goes here ifs >> value; } //改进 while(ifs >> value){ //ifs.getline(line,80); }
-
函数:int sum_arr(int arr [] , int n); int sum_arr(int* arr,int n);
-
arr[i] = * (arr + i); &arr[i] = arr + i;
-
遍历保护数组 void show_array(consr double arr [],int n);
-
使用数组区间的函数:
const int size = 8; int arr [8]; int sum = sum_arr(arr,arr+size); //arr指向第一个元素位置 arr+size指向最后一个元素的后一个位置 //函数 int sum_arr(const int* begin,const int* end){ const int* pt; int total = 0; for(pt = begin;pt!=end;pt++) total = total + *pt; return total; }
-
const int* pt = &age; 防止用指针修改指向的值*pt=1不被允许
-
int* const pt = &age; 防止指针指向其他位置pt=&other不被允许
-
内联函数inline,不可以递归
-
inline double square(double x){ return x * x;}
-
引用变量:必须在声明时进行初始化
-
c++11右值引用:double && rref = std::sqrt(36.00);
-
默认参数:char* left(const char* str,int n = 1); 从第一个默认参数向右必须都是默认值
-
设置新串长度:
int len = strlen(str); n = (n<len)?n:len; char* p = new char[n+1];
-
函数重载
-
函数模板:template void swap(AnyType &a,AnyType &b);
-
重载的模板:
template <template T> void Swap(T &a,T &b); template <template T> void Swap(T* a,T*b,int n);
-
显式具体化:template <> void Swap(job &j1,job &j2);
-
非模板函数优先于具体化优先于常规模板函数
-
强制转换:(非引用条件下)
template <template T> T Add(T a,T b){ return a+b; } int m = 6; double x = 2.01; cout<<Add<double>(m,x);
-
是什么类型:c++11
template <class T1,class T2> void f(T1 x,T2 x){ ... ?type? xpy = x + y; ... } //改进:makexpy the same type as x+y decltype(x+y) xpy = x + y;
-
decltype (expression) var; 若expression是函数,则类型为函数的返回类型 但不会调用函数
-
如果expression是一个左值,则var为指向其类型的引用
-
如果需要多次声明可结合typedef使用:typedef decltype(x+y) xytype; xytype xpy = x + y;
-
c++11后置返回类型
template <class T1,class T2> ?type? f(T1 x,T2 x){ ... return x + y; ... } //c++11新增后置返回类型 double h(int x,float y); //等价于 auto h(int x,float y)->double; template <class T1,class T2> auto f(T1 x,T2 x) -> decltype(x + y){ ... return x + y; ... }
-
寄存器变量register int count_fast; 提高访问变量的速度 c++11register只是指出变量是自动的
-
当 类或结构为const 时,可以用mutable声明改变内部修改
-
内置类型分配存储空间并初始化int* pi = new int (6);
-
初始化常规结构及数组:
struct where{double x,double y,double z}; where* one = new where{1.1,2.2,3.2}; int* ar = new int [4] {2,1,2,2};
-
c++11将列表初始化用于单值变量:double* pdo = new double {99.0};
-
抽象和类:
//类声明: class Stock{ private: //防止数据的直接访问 string m_name; int m_age; void set_lot(){total_val = shares * share_val;} public: //构造函数 Stock(){m_name = "name";age = 0;} Stock(string name,int age); void show(); //只能通过公有成员函数式访问对象的私有成员 }; //实现类成员函数 Stock::Stock(string name,int age){ this->m_age = age; this->m_name = name; } void Stock::show(){ //类方法可以访问类的private组件 cout<<age<<name; }
-
通常使用私有成员函数来处理不属于公共接口的实现细节
-
private关键字是类对象的默认访问控制
-
其定义于类声明中的函数自动称为内联函数
-
使用构造函数:
Stock food = Stock("name",1); Stock food("name",1); Stock* pstock = new Stock("name",1);
-
构造函数被用来创建对象但不能通过对象来调用
-
默认构造函数:Stock::Stock(){} 当且仅当没有定义任何构造函数时调用默认构造函数,但定义了非默认构造函数后,就必须也提供默认构造函数
-
设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数
-
区分:
Stock first("name",1);//非默认构造 Stock second; //默认构造 Stock third();//返回值为Stock类型的一个函数
-
析构函数:Stock::~Stock(){}
-
当stock1存在时,stock1 = Stock(“a”,2); 是将新值赋给stock1
-
如果既可以通过初始化也可以通过赋值设置对象值,应采用初始化,效率更高
-
const Stock land = Stock("name",2); land.show();//不被调用 //下面,保证函数不会修改调用对象: 函数声明:void stock::show() const //promisesnot to change invoking object 函数定义:void Stock::show() const{} //这样就可以执行第2行代码了
-
this指针:两对象比较返回较大值的引用
const Stock& topval(const Stock& s)const{ if(s.age > age) return s; return *this; } Stock top = stock1.topval(stock2);//隐式访问stock1 显式访问stock2
-
对象数组
-
作用域为类的常量:
class Vakery{ private: const int Months = 12; //错误写法 double costs[Months]; }; class Vakery{ private: enum {Months = 12}; //正确写法 double costs[Months]; }; //枚举只是创建符号常量,并不打算创建枚举类型的变量,因此不必提供枚举名 C++98写法 class Vakery{ private: static const int Months = 12; //正确写法 double costs[Months]; };
-
抽象数据类型
-
运算符重载
Time Time::Sum(const Time& t)const{ ... return sum; //传递引用效率更快 //返回值为sum对象,不能返回指向局部变量的引用 } //重载加法运算符 声明:Time operator+(const Time& t) const; Time operator+(const Time& t) const{ //将结果用作方法名即可 return time; } total = coding + fixing; //运算符左侧为调用对象,右侧为传递参数
-
友元访问类中私有属性:友元函数 友元类 友元成员函数 通过让函数成为友元,可以赋予该函数与类的成员函数相同的访问权限(访问私有属性)
Time Time::operator*(double mult) const{ //将结果用作方法名即可 Time time; ... return time; } //这里只能使用 A = B * 2.75 不能使用A = 2.75 * B //利用友元进行改进:非成员函数调用对象 所有值都是显式参数 函数原型:Time opetator*(double m,const Time& t); //这样,第一个参数为运算符左值,第二个参数为右值 即A = 2.75 * B //但是,非成员函数不能访问类内的使用数据 //一类特殊的非成员函数:友元函数!
-
创建友元
//放在类声明中, firend Time operator*(double m,const Time& t);
-
如果要为类重载运算符,并将非类的项作为第一个操作数,则可以用友元函数来反转操作数的顺序
-
重载<<运算符
void opetator<<(ostream& os,const Time& t){ os<<t.hours<<"hours"<<t.minutes<<"minutes"<<endl; } cout<<t; //改进 ostream& opetator<<(ostream& os,const Time& t){ os<<t.hours<<"hours"<<t.minutes<<"minutes"<<endl; return os; } cout<<"Trip time:"<<t<<endl;
-
重载运算符作为成员函数还是非成员函数?
Time operator+(const Time& t)const; friend Time operator+(const Time& t1,const Time& t2);
-
如果方法通过计算得到一个新的类对象,则应考虑是否可以使用类构造函数来完成
-
对已重载的运算符进行重载
-
类的自动转换和强制类型转换
-
静态数据成员
//静态数据成员在类内声明,类外初始化 class StringBad{ private: static int num_strings; } int StringBad::num_string = 0; //输出 StringBad str; cout<<str.num_strings<<endl; cout<<StringBad::num_string<<endl; //静态方法只能访问静态成员变量 不能访问普通变量和普通方法 但是非静态方法都可以访问
-
在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete进行释放
-
带参数的构造函数也可以做默认构造函数,但所有参数都要有默认值 如:Klunk(int n = 0){klunk_ct = n;} 这样就不能再由Klunk(){klunck_ct = 0;}.
-
c++const解析
//const type name = value; 等价于 #define name value //类外声明的使用.h:(extern) const int bufSize = 512; //类内使用:int const buf = 1024;或const int buf = 1024; /*const和&: int val = 2; const int& refval = val; 非const对象可以给const引用 引用不能随便改变被引用的对象,即不能利用refval改变val值 那么refval = 3是错误的,但这里可以用val改变他的值,即val = 3; int val2 = 3; refval = val2;也是错误的,这里也是试图改变被引用对象的值 */ /*const和指针: const int* p = NULL; int val = 1; p = &val; 可以进行 *p = 2; 不可以操作 int val2 = 2; p = &val2; 可以进行 *p = val2; 不可以操作 即const int* p = NULL;限定了p所指向位置的值不能改变 但是p指针所指向的位置依然是能被改变的 int val = 1; int* const p = &val; int val2 = 2; *p = val2; 可以进行 *p = 3; 可以操作 p = &val2; 不可操作 p = NULL; 不可操作 即int* const p = &val;限定了p所指向的位置不可改变,但是p指向的位置的值是可以被改变的 如果const int val = 1;那么只能有const int* p = &val; 而int* const p = &val;报错 如果const int val = 1;那么可以有const int* const p = &val; 表示p位置和位置的值均不能改变 当然int val = 1;前面没有const也可以使用const int* const p = &val;即值和指针指向的位置均不能发生改变。 */ /* void list(){ 相当于 void list(className* const this) //普通类方法 this->data = other; 可以被改变 } void list()const{ 相当于 void list(const className* const this) //普通类方法(常方法) this->data = other; 不可以被改变! } 普通方法可以调用常方法和普通方法 常方法只能调用常方法 */ /* 指向const对象的引用 提高效率:如果函数返回(通过调用对象的方法或将对象作为参数)传递给他的对象,那么可以采用返回引用 const Vector& Max(const Vector& v1,const Vector& v2){ if(v1>v2) rerurn v1; else return v2; } 传参为const&: void function(const int& num){ num = 5; //不可修改值 } 返回值为引用: int sum = 0; int& add(const int& a, const int& b){ sum = a + b; return sum; } cout<<sum<<endl; 不要返回局部变量的引用 函数引用可以做为左值 */ /*
-
const、指针、引用的类方法剖析
class Test{ public: int data; Test(int n){ data = n; } int getData(){ cout<<"普通方法"<<endl; return data; } int getData()const{ //不可改变类的值 cout<<"常方法"<<endl; return data; } int& getRef(){ cout<<"普通方法-返回引用"<<endl; return data; } const int& getRef()const{ //说明:前面需要加上const 因为常方法不能对内容更改,所以返回的引用也不可以更改其内容,用const限定 cout<<"常方法-返回常引用"<<endl; return data; } int* getPtr(){ cout<<"普通方法-返回指针"<<endl; return &data; } const int* getPtr()const{ cout<<"常方法-返回常指针"<<endl; return &data; } }; int main(){ Test t1(10); //当然也可以调用常方法 const Test t2(100); int a1 = t1.getData();//普通对象调用普通方法 返回一个常量用a接收 cout<<a1<<endl; //int &b1 = t1.getData();//普通对象调用普通方法 用引用来接收一个返回常量值报错 const int &c1 = t1.getData();//普通对象调用普通方法 常属性接收 c1不可更改 cout<<c1<<endl; cout<<"======="<<endl; int a2 = t2.getData();//常方法 返回常量data赋值给a2 cout<<a2<<endl; //int &b2 = t2.getData();//常方法 返回常量data给b2引用出错 const int &c2 = t2.getData();//常方法 返回常量data赋值给引用常量c2 c2不可更改 cout<<c2<<endl; cout<<"======="<<endl; cout<<"======="<<endl; Test t3(1000); const Test t4(10000); int a3 = t3.getRef();//普通方法 且通过a3不会改变t3.data值 cout<<a3<<endl; int &b3 = t3.getRef();//普通方法 传递引用 通过b3会改变t3.data值 若普通方法的返回为const int&则会报错 即不能进行t3.data的改变 cout<<b3<<endl; const int &c3 = t3.getRef();//普通方法 传递引用 通过c3不会改变t3.data值 cout<<c3<<endl; cout<<"======="<<endl; int a4 = t4.getRef();//常方法 且通过a4不会改变t4.data值 cout<<a4<<endl; //int &b4 = t4.getRef();//常方法 返回为const int&则会报错 即不能进行t.data的改变 const int &c4 = t4.getRef();//常方法 不能进行c4的修改 即不能修改t4.data cout<<c4<<endl; cout<<"======="<<endl; cout<<"======="<<endl; Test t5(11111); const Test t6(19999); cout<<"t5.data的地址为:"<<&t5.data<<endl; cout<<"t6.data的地址为:"<<&t6.data<<endl; int* a5 = t5.getPtr(); //接收t5.data的地址 可以改变t5.data cout<<a5<<" : "<<*a5<<endl; //int* &b5 = t5.getPtr();//报错 使用引用b5会改变指针的位置 //const int* &c5 = t5.getPtr();//const放在前面是对data本身的限制 这里改为const int* const &c5 const int* const &c5 = t5.getPtr();//c5接收t5.data地址 且不可修改其值(去掉前面第一个const限制可以修改) //第一个const限定c5指向位置的内容不能变,第二个const为限制c5指向位置不变 cout<<c5<<" : "<<*c5<<endl; cout<<"======="<<endl; //int* a6 = t6.getPtr();//报错 用a6可以改变其data值 但函数const不允许这样做 //int* &b6 = t6.getPtr();//报错 接收指针时出错,再对其引用更加错误 //const int* &c5 = t6.getPtr();报错 const int* const &c6 = t6.getPtr();// 这里对指针指向位置和其内容必须全部封锁 不可修改 cout<<c6<<" : "<<*c6<<endl; return 0; } //测试: /* 普通方法 10 普通方法 10 ======= 常方法 100 常方法 100 ======= ======= 普通方法-返回引用 1000 普通方法-返回引用 1000 普通方法-返回引用 1000 ======= 常方法-返回常引用 10000 常方法-返回常引用 10000 ======= ======= t5.data的地址为:0x69fe8c t6.data的地址为:0x69fe88 普通方法-返回指针 0x69fe8c : 11111 普通方法-返回指针 0x69fe8c : 11111 ======= 常方法-返回常指针 0x69fe88 : 19999 Process returned 0 (0x0) execution time : 0.026 s Press any key to continue. */
-
左移右移运算
int a = 10; int b = a>>1; //除以2 得5 int c = b<<2; //乘以2再乘以2 得20 cout<<b<<endl; cout<<c<<endl;
-
派生类:class RatedPlayer : public TableTennisPlayer{}
-
共有派生,基类的公有成员将成为派生类的公有成员,基类私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问
-
派生类可以有自己打的构造函数,可以额外添加数据成员和成员函数
-
构造函数必须给新成员和继承的成员提供数据
class RatedPlayer : public TableTennisPlayer{ private: int rating; public: RatedPlayer(int r = 0,const string &fn = "none",const string &ln = "none",bool ht = false); RatedPlayer(int r,const TableTennisPlayer &p); } //构造函数实现: RatedPlayer::RatedPlayer(int r,const string &fn,const string &ln,bool ht):TableTennisPlayer(fn,ln,ht){ rating = r; } RatedPlayer::RatedPlayer(int r,const string &fn,const string &ln,bool ht){ //如果不调用基类构造函数,将使用默认的基类构造函数:TableTennisPlayer() rating = r; } // RatedPlayer::RatedPlayer(int r,const TableTennisPlayer &tp):TableTennisPlayer(tp){//复制构造函数 rating = r; } //当然也可以对派生类成员使用成员初始化列表语法:第二个构造函数编写如下: RatePlayer::RatePlayer(int r,const TableTennisPlayer &tp):TableTennisPlayer(tp),rating(r){}
- ——————END-2022-03-17——————
- 个人学习笔记,如有纰漏,敬请指正。
- 感谢您的阅读。