目录
1.c++入门知识点
1.::域限定作用符,如果左边什么都没有写,表示在全局找
int a=5; int mian() { int a=1; cout<<a<<endl;//这个打印的结果是1 cout<<(::a)<<endl;//使用了域作用限定符,在全局里面找a,使用打印的结果为5 }
2.缺省参数不可以在声明和定义同时出现,一般推荐在声明给,定义时就不要给了,因为如果当声明和定义时给不一样的缺省值,我们就不知道改用哪个了。
3.返回值类型不能构成函数重载,因为在调用的时候编译器不能很好的进行区分
void func(int a,int b) { } int func(int a,int b) { return b } int main() { func(3,5); func(5,6); }
4.引用的 使用规则
(1)引用定义的变量必须要初始化
(2)引用只能引用一个实体,不能指向多个实体
(3)引用可以给被引用的变量再次进行引用
5.只要出了作用域还在的变量,就可以使用引用返回
6.传引用的一个好处就是可以改变传引用值,另一个好处就是可以减少临时拷贝
7.这个是错误的,因为ret是引用 而count是传值返回,返回的是临时变量,临时变量具有常性,不能被修改,如果要变成正确的话就要加个const修饰 这是权限的平移 引用和指针的权限是可以平移和缩小的 但是不可以放大
//正确写法:const int& ret=count(5);int count(int c) { c++; return c; } int main() { int& ret=count(5) }
8.像强制类型转化和隐式类型转化都是会产生一个临时变量的
例如int a=10; double& b=i;//是错误的,因为这是强制类型转化,会产生临时变量
const double& b=i;//是正确的
9.内联函数不建议声明和定义分离,一般建议直接在.h里面写
10.typeid(b).name()这个函数的作用是获取b的类型
例如如果是int 就获取到int类型,如果是int*,就获取到int*类型
11.范围for—语法糖写法int array[]={1.2.3.4.5.6.7}; for(auto e:array)//不一样要用e,用其他变量也可以表示 { cout<<e<<endl; } //这个是赋值给e对象,数组里面的内容是不会改变的 如果写成auto& e:array就会改变e的值 范围for不适应一种情况 void func(int array[]) { for(auto x:array) { cout<<x<<endl; } } //因为传参过去后array就变成了指针 而指针不可以使用范围for
12.auto不能使用的场景
(1)不能用于函数传参,例如void func(auto a)
(2)不能用于声明数组的类型
(3)auto可以定义多个变量,但是每个变量的类型都必须一致,例如auto a=1,b=1.1是不可以的
13.在c++里面空指针要用nullptr,而不用NULL,因为在c++里认为NULL是0
14.使用class类的时候,默认是private,私有的意思是不限定类里面的权限,而是限制类外面的权限,让外面的人不能访问到里面
15.struct类默认是public,因为要兼容c语言
16.声明和定义的区别:声明就是那个还没有开空间,而定义就是实实在在的开了空间
17.每个对象的成员变量都是不一样的,需要独立存储,而每个对象的成员函数是一样的,放到共享公共区域(代码段),这个放到代码段指的是指令在代码段 而函数的参数和局部变量(指令运行起来需要的相关数据)还是在栈区。
18.当一个类里面没有成员变量的时候,它的大小为1,这个1个byte不存储有效数据,仅仅是为了占位,表示实例化对象存在过
19.c++类里面有一个this的指针,只不过这个this的指针是由编译器做的,不能由我们控制
例如 void func(date* this,int year,int month ,int day),这个this指针被隐藏了,不会显示出来
20.this指针可以为空,但是不能对空指针进行解引用否则就发生错误
void func()
{}
例如 Date* ptr=nullptr,ptr->func();//正常运行,因为没有对空指针进行解引用,这个用法同于(*ptr).func()
21.如果声明和定义分离,那么定义函数就要加上域作用符例如 void Date::func()
{},而如果都写在一个类里面就不要添加
22.构造函数是一个特殊的函数,他是由编译器自动调用的,且构造函数的名字与类名相同,没有返回值(意思是不用写void),构造函数可以重载,构造函数传无参的时候写法是Stack st;有参数的时候是Stack st(4);传无参的时候不能带括号的原因:这样做会与函数的声明(返回值是Stack ,函数名是st)一样,编译器不能很好的理解他是要实例化对象还是要声明函数。
23.C++里调用类里面的函数的写法是st.push(1);而C语言是stackpush(1)。
2.类和对象的知识点
1.析构函数的作用不是完成对象本身的销毁,而是完成对资源对象的清理,像new malloc 这样的就需要析构函数,如果不需要清理的话,就不会调用析构函数,析构函数不能支持函数重载
2.构造函数如果我们不写,编译器会自动生成,如果写了,那么编译器不会自动生成(但凡我们只要写了一个有参数的构造函数,但是没有写缺省值,如果传无参的,那么编译器会报错),并且编译器自动生成的构造函数对内置类型不作任何处理(随机值),但是对自定义类型会完成初始化
3.如果一个类里面是内置成员,虽然不能生成构成函数,但是我们可以在声明得时候给缺省值,但是要注意这不是初始化
4.如果是内置类型和自定义类型混用得话,那么编译器还是只会对自定义类型完成初始化,但是对内置类型还是不作处理
5.无参构造函数,全缺省构造函数,和编译器默认生成的构造函数都可以叫做是默认构造函数,默认构造函数可以认为是不传参就可以调用构造函数,一般建议,每个类都提供一个默认构造函数
6.拷贝构造函数是构造函数的一个重载函数,拷贝构造函数必须的参数只有一个且必须是类类型对象的引用,否则会引起无限拷贝构造函数
构造函数写法
Date(int year=1)
而拷贝构造函数写法是Date (Date& d)
{
_year=d._year
}
7.对于自定义类型的传参都要调用拷贝构造函数,例如class Date { public: Date(Date& d) { _year=d._year; } } void Func(Date d) { } //如果是传引用传参就不需要调用拷贝构造, //因为d是d1得别名,并且指针也不需要,因为指针是内置类型 void func(Date& d1) {} int main() { Date d1; func(d1); func2(d1); }
这边我们使用func函数 但是因为传参的类型是自定义类型 所以编译器会调用拷贝构造函数 会进入到Date类里面,当拷贝构造函数完成后才会继续到func函数里面,这种也被称为深拷贝构造,如果是内置类型,就是浅拷贝构造,浅拷贝构造编译器可以自己驾驭,但是深拷贝构造不行
8.拷贝构造有两种写法,一种是Date d2(d1);另一种是Date d2=d1;
9.在类里面有一个this隐藏的指针,当我们会返回这个对象时,我们要返回*this,注意这个*不是代表解引用,而是表明this是这个对象的本身10.C++类里面的析构函数也满足一个栈的特性,也就是先进后出,就是说先进来的后析构,后进来的先析构
11.析构栈的时候,如果用浅拷贝的话,就会导致array指向同一块空间,然后st2先析构,会把array的空间析构,再把st2.array置成空,但是st1.array变成了野指针,因为st2只是把自己的置成空,并不会改变st1的类成员变量值
12.什么情况下需要实现拷贝构造?
自己实现了析构函数释放空间,就需要实现拷贝构造
13.使用运算符重载打印的时候要注意<<的优先级会有些运算符高一些,因此打印的时候一般都建议加上括号
例如cout<<(d1==d2)<<endl
14.注意,这五个操作符不能运与函数重载:
(1).* (2):: (3) sizeof (4) ?:(5) .
15.Date d2=d1是拷贝构造
d1=d2是赋值运算符
16.写赋值运算符的时候我们函数的返回值一般要求返回*this这个值,这个目的是为了支持连续赋值 d1=d2=d3
17.在C++中,写一个运算符重载时,可能遇到前置++和后置++的问题,因此编译器会默认再后置++的函数参数里添加一个int参数,这个是固定的,并且他没有实际的意义,只是为了能构成运算符重载
18.<<流插入其实是个运算符重载,但是自定义类型不支持,因此当我们写一个类的时候,一般都要需要自己写cout<<d1这种类型的运算符函数
19.当我们把一个类函数写在类里面,那么默认第一个是this,但是我们如果写在外面就为全局函数,那么参数的顺序就是我们可以控制的,但是作为全局函数,是不可以直接访问私有成员的,所以C++访问私有成员一般喜欢用一个友元函数friend,friend void operator《(ostream& out,const Date& d),为了能支持连续赋值,要把void operator《(ostream& out,const Date& d)改成ostream& operator<<(ostream& out,const Date& d)返回值为out
20.写流提取的函数为istream& operator<<(istream& out,Date& d)返回值为in
21,内联函数是不会进符号表的,当我们声明和定义分离的时候 就会call 符号表去找定义,而又因为内联是不进符号表的 所以会发生链接不上的现象
22.当我们在类里面写函数定义的时候 编译器一般都会把较短的函数当做内联函数来处理
23.
class A
{
public:
void printf()const
{
cout<<_yaer<<endl;
}
}
int main()
{
const A aa;
}
这边要加const的原因是 我们实例化对象aa是const A,所以我们不能进行权限的放大,但是因为*this是隐含的参数 我们不能直接改变,因此只能间接添加一个const
24.c++还有一个运算符&也是天选之子,但是实际没有多少意义,一般都不要要求写
Date& operator&()
{
return this;
}
25.初始化列表:以冒号为开始,以逗号为分割的数据成员列表,每个成员列表后面的扩号表示初始化值或者表达式,这个一般是用来成员变量里含有被const修饰的对象成员,因为被const修饰的变量只有一次初始化的机会,所以在被定义出来的时候就必须要初始化class A { public: A() :_x(5) ,_a2(5) { } private: int _a1=2; int _a2=6; const int _x; }
private里面的是缺省值,而构造函数里面的才是定义初始化,也就是说如果初始化列表里面有给初始化的就用初始化,没给得话就是用缺省值
26.有三个类型必须在初始化列表定义
1.const 例如 const int _x
2.引用 例如 int & ref
3.自定义成员(且该类没有默认的构造函数)例如class B { public: B(int x) :_b(0) { } private: int _b; } class A { public: A() _bb(5) { } private: B _bb; }
27.成员变量在类中声明次序就是其再初始化列表中的初始化顺序,与其再初始化列表中得先后次序无关class A { public: A(int a) :_a1(a) ,_a2(a1) { cout<<_a1<<_a2<<endl; } private: int _a2; int _a1; } int main() { A aa(1); }
这个结果是1和随机值,因为a2先被定义出来 所以2就要先被初始化 而a1这时候是还没有完成初始化的 所以结果就为随机值