文章目录
1. if的短路效应:
如果&&
前面条件不满足,或者||
前面条件满足了,那么直接跳出,不做后续操作,意思是如果后面有赋值也没用啦
2. 局部变量会覆盖全局变量
3. do{ }while()
语句是先执行一次,然后看符合条件继续,不符合跳出
4. while小心数组越界!!!
毕竟for里面有判断越界的条件,while
里面很容易忘记写
5. 缺省值
要从最右边的变量开始写,右边的有缺省值了左边才可以有缺省值
6. 内存分配
7. 一些重载
- 下标运算符
int & IntArray::operator[](int index){
(index < low || index > high){
cout << "下标越界"; exit(-1); }//越界就直接停掉了
return storage[index - low];
//返回的是引用类型,来修改数据元素
}
- 函数调用运算符
(用圆括号里面写参数值,来对某个对象进行操作,视作函数)如调用arr1 = arr(12, 15, 2)
,其中arr和arr1都是Intarray类的对象
IntArray operator()( int start, int end, int lh){
if (start > end || start < low || end > high ) {
cout << "下标越界"; exit(-1);
}
IntArray tmp(lh, lh + end - start);
for (int i = 0; i < end - start + 1; ++i)
tmp.storage[i] = storage[start + i - low];
return tmp;
}
- ++&–
成员函数
++ob重载为:ob.operator++()
ob-- 重载为:ob.operator–(int)
友元函数
++ob重载为:operator++(X &ob)
ob–重载为:operator–(X &ob, int)
调用时,参数int一般传递给值0。
Counter & Counter::operator++(){
if (value == alarm) cout << "已超过报警值\n";
else {
++value;
if (value == alarm) cout << "已到达报警值\n";
}
return *this; //返回修改后的状态
}
Counter Counter::operator++(int x){
Counter tmp = *this; //保存对象修改前的状态
if (value == alarm) cout << "已超过报警值\n";
else {
++value;//这里*this其实是改变了的!
//因为是引用了++前的参数,也就是main里面的cnt,所以是可以直接改变的,只是return的时候return的是++之前的情况。
if (value == alarm) cout << "已到达报警值\n";
}
return tmp; //返回修改前的状态,为什么不是返回引用:因为局部变量不可以返回引用
}
int main(){
Counter cnt(3); //定义一个Counter类的对象,报警值为3
cnt.print(); /显示对象的当前值,此时输出为0
++cnt;
cnt.print(); // 此时输出为1
(++cnt).print(); //调用前缀的++,输出2
(cnt++).print(); //调用后缀的++,当前对象的value已经加1报警。但输出的是2
cnt.print(); //输出值为3
return 0;
}
- 输入输出流
ostream & operator<<(ostream & os, const ClassType &obj){
os << 要输出的内容;//有时候不加const会出问题的!
//用os,不写cout,因为有时候不是屏幕输出!
//只进行最小限度格式化,由用户控制输出细节,不应输出换行符
return os;
}
istream & operator>>(istream & is, ClassType &obj){
//这里第二个形参千万不要加const
is >> 要输入的内容;
return is;
}
- 类型转换
将单参数的构造函数定义为explicit,将告诉编译器不允许执行隐式转换。
operator 目标类型名 ( ) const
{ …
return (结果为目标类型的表达式);
}
8. 重载和重定义
重载的意思是:使参数个数不同、参数类型不同或两者兼而有之的两个以上的函数取相同的函数名;重定义是在子类里面重新写一个参数表都一模一样的函数
9. 友元:
为了访问私有成员而开的后门,有友元函数(一般函数)、友元成员(成员函数)、友元类;
声明在类内friend fun(参数表);
,但具体定义在哪都行:friend 函数返回类型 类名标识符::函数名(参数列表);
注:参数表甚至可以不写类型后面的变量名,只写类型就ok
10. 成员函数put输出字符
cout.put('A');
和cout.put(65);
都输出A;可以写cout.put('A'),put('\n');
意思是可以重复写put语句,因为put的返回值是当前对象的引用。
11. write成员函数无格式输出
char buffer[]="HAPPY BIRTHDAY";cout.write(buffer,10);cout.write("ABCDEFGHIJKLMN",10);
都是输出前十个字母的意思。
12. 输入流:
cin>>
跳过开头的空白字符(space,tab,\n),开始读取,直到读到空白字符作为结束符,该结束符仍留在缓冲区里。
cin.get();
读入一个字符,包括space和EOF,并将读入值作为函数返回值。
cin.get(a);
读入一个字符,存储在参数a中,返回值是当前输入流对象的引用。
cin.get(ch,80,'\n');
表示输入字符串,结束符默认为'\n'
(可以不写),结束时自动插入一个'\0'
到末尾。注意结束符仍在缓冲区中!
cin.getline(ch,80,'.');
输入字符串,结束符不留在缓冲区中。
13. cin.read(buffer,10);
无格式输入。
14. 文件操作:
- open
- 文件的随机读写
文件定位指针(long)类型,in.tellg();
返回in的读文件定位指针;out.tellp();
返回out的写文件定位指针。
文件定位指针成员函数:in.seekg(10);
是指in指针找到第10个位置,从第11个字节开始读;out.seekp(10);
是指out指针找到第10个位置,开始在第11个字节上写东西进去,效果是覆盖原来的。 - 流式文件处理含记录的文件
15. 函数模板
定义方法:
template<class T>
T max(T a,T b){
return a>b ? a:b;
}
实例:
template <class T>
T power(T base, int exponent){
T result=base;
if (exponent==0) return (T)1;
if (exponent<0) return (T)0;
while(--exponent) result*=base;
return result;
}
注意:不允许自动进行类型转换,要是传进去两个数据类型不一样,就是编译错误。
注:如果有对应类型的非模板函数,则优先调用非模板函数;没有才用模板函数。
16. 带const的指针
`const int *p=&a; int const *p=&a;` p是指向const int的指针,意思是a不能变,但p可以变
`int *const p=&a;` p是const,意思是p始终指向a,但a可以变
17. static
- 静态数据成员:对类中所有变量共用。
静态数据成员不属于对象的一部分,而是类的一部分;静态数据成员的初始化不能放在类的构造函数中;类定义并不分配空间,空间是在定义对象时分配;但静态数据成员属于类,因此定义对象时并不为静态成员分配空间。类的实现时为静态数据成员赋值。
可以通过作用域操作符从类直接调用。如:SavingAccount::rate
但从每个对象的角度来看,它似乎又是对象的一部分,因此又可以从对象引用它。如有个SavingAccount类的对象obj,则可以用:obj.rate
由于是整个类共享的,因此不管用哪种调用方式,得到的值都是相同的 - 静态成员函数:为类的全体对象服务,而不是为某个类的特殊对象服务
由于静态成员函数不需要借助任何对象就可以被调用,所以编译器不会为它暗加一个this指针。因此,静态成员函数无法处理类中的非静态成员变量。//非静态函数可以操作静态成员
静态成员函数的声明只需要在类定义中的函数原型前加上保留词static。
类外定义static函数的时候只需要写类的名字::函数名即可,不需要加static。 - 静态的常量数据成员:整个类的所有对象的共享常量
静态的常量数据成员的声明:
static const 类型 数据成员名 = 常量表达式;
注意const数据成员和static const数据成员的区别 :const是每个变量一个,可以不同,static const必须都一样 - 关于析构顺序做一个很完整很完整的总结:
#include <iostream>
using namespace std;
int a;
class A {
public :
A(int x){
tmp = x;
cout << "tmp : " << tmp << endl;
}
~A(){
cout << "tmp " << tmp << endl;
}
void f(){
A a7(7);
static A a8(8);
A a9(9);
}
private :
int tmp;
};
A a1(1); // 全局变量;
static A a2(2);
A a3(3);
int main()
{
A a4(4);//局部变量;
static A a5(5);//静态局部变量;
a5.f();
static A a6(6);
a6.f();
return 0;
}
运行结果:
tmp : 1
tmp : 2
tmp : 3
tmp : 4
tmp : 5
tmp : 7
tmp : 8
tmp : 9
tmp 9
tmp 7
tmp : 6
tmp : 7
tmp : 9
tmp 9
tmp 7
tmp 4
tmp 6
tmp 8
tmp 5
tmp 3
tmp 2
tmp 1
说明:
- 非main函数中,非静态的在函数结束时析构,顺序倒过来
- main函数中的非静态析构,顺序倒过来
- main函数倒着走,遇到静态就析构,遇到局部函数最后一次出现的时候到里面去析构掉里面的静态成员(倒着)
- 局部变量全部析构完了,现在去析构全局和全局静态
- 直接倒着走,全局变量不区分是否是静态
18. 关于初始化列表
有的时候,你的类是继承来的,用初始化列表调用基类的构造函数事半功倍
可以看看这篇文章,里面讲了不止两种情况,继承类也要用初始化列表
19.一些知识点
- 析构函数只能有一个,不能重载
- 字符串:实验验证,用
char ch[]="abcd";
的空间开了5个,加上了\0;char ch[] ={'a','b','c','d'};
的空间开了4个,大概是没加\0;如果开多了又没全部定义,则后面都补0; - 整数补码表示
- 指向结构体的指针声明时不开结构体空间的
- 为什么我的文章还没有审核通过,我想考前在手机上看可不可以
20. 字符串与指针
- 指针使用前必须初始化!!!!!!!不初始化不许用!!!!包括strcpy什么的,连空间都没指的话根本用不了!!如果你写
char *p;p[2]='a';
也是错的,因为根本没有指向的空间 - 已经指向常量区字符串的指针不能用strcpy,因为strcpy是针对指针指向的地方进行修改,而常量区不可以修改!!!
- 字符数组的数组名也是常量指针,不可以进行修改.如
char ch[]="xyz";ch="abc";
是不对的,因为没法修改常量指针的指向。