类模板的成员函数本身就是模板函数,无需再次声明。
类模板的成员函数本身就是模板函数,无需再次声明。
1、类型限制在于函数对于该参数做了什么操作
临时对象
接管(不拷贝,使原先对象失去内存控制权)
C++ 右值引用
赋值运算符左边:左值(地址)
赋值运算符右边:右值(内容)
右值引用:对临时对象的引用
语法:
s3=s1+s2;
hz::string_pro& lstr = s3;
hz::string_pro&& rstr = s1+s2;
移动语义:提高性能,并且不需要改变语法。
const string_pro& another
string_pro&& another
不能用const,因为我们需要修改another的内容与指针
1、C字符串最后字符为/0
2、字符串后面预留空间,这样追加字符性能高
1、初始化对象时:
hz::string_pro s1="hi";
hz::string_pro s2("hello");
均调用同一构造函数,两种操作等价。
2、拷贝该构造函数
hz::string_pro s2(s1);
编译器会默认生成一个拷贝构造函数,但是通常会造成编译问题,因为其拷贝方式是逐位拷贝,只包含成员变量,不包含堆上内存数据。如果成员变量指向同一段内存,而析构时会出现两次析构,针对同一段内存进行delete操作。
3、什么时候需要拷贝构造函数:
(1)需要用一个对象对新建对象进行初始化时
(2)函数中使用对象传值作为形参输入
(3)函数将对象传值作为返回值
4、编译器生成的赋值运算符:逐位拷贝
hz::string_pro s2;
s2=s1;
5、拷贝构造函数与赋值运算符是成对出现的
1、堆(heap) GB/TB级别
2、静态变量 全局变量 既不在堆上又不在栈上
3、hz::string_pro s1 = "hihi";
s1本身在栈上,管理内存在堆上
4、std::cout<<sizeof(hz::string_pro)<<" "<<sizeof(s1)<<std::endl;
string_pro 两个int类型的成员变量,所以大小为8;虽然s1对象被初始化为“hihi”理论上应该输出8+4=12,但是sizeof()只计算栈上内存,不计算堆上内存。
5、在编译阶段就可以确定函数占用内存的大小,栈上操作仅仅相当于移动指针,但是堆上操作比较慢,需要向操作系统申请内存。(可能有百倍的差距)
6、动态分配内存:指定一个大小,分配指定大小的内存
1、多线程安全:12306买票,显示还有票,但是实际上没有了。解决措施:加锁操作(本身违背多线程思想)可能导致死锁发生(A需要B占用的资源,B需要A占用的资源,如果加锁,则对象对数据操作期间无法被剥夺,会陷入循环等待)
2、资源管理原则 RAII
class Lock{
Lock(){
pread_mutex_lock();
}
//伟大的析构函数!跟对象生命周期一致,作用域结束,抛出异常
~Lock(){
pread_mutex_unlock();
}
};
//数据库打开关闭,网络打开与关闭
3、Resource Acquisition Is Initialization
资源获取即是初始化
1、zero-overhead abstraction
零开销抽象,C++提供更多抽象机制,但不增加开销。
2、函数定义直接写在头文件里面,默认内联
3、delete nullptr 是允许的,如果delete后面接一个空指针则无事发生
4、_data = new char[clen];
..........
delete[] _data;
new[] 对应 delete[] (针对数组)
new 对应 delete (针对对象)
5、new char[0] 也是允许的
看到7m左右
1、std::string,拥有字符串
std::string_view,仅存储起始指针和长度
2、std::wstring 宽字符串用来存储汉字,有可能是2个字节,或者4个字节,这个跟系统位数有关
3、Utf-8是变长编码,最长占6个字节,最短占1个字节
4、如果对象是一个const类型,则被调用的其成员也要声明为const类型
1、运算符重载作用:让用户自定义的类也能使用内置的运算符
2、字符串重载“=”时,采用的是复制操作
3.s1.operator+=(s2);
s1.operator[](0)='q';
4、加法运算符重载不是成员函数,定义为非成员函数可以扩大有效范围。
5、operator“+”支持string类型与C字符串相加
6、运算符重载必须要有一方是用户自定义类型
7、cout<<s1<<s2;
operator<<(operator<<(cout,s1),s2);
8、//bool类型转换运算符重载
while(cin){
cin>>num;
}
9、不能被重载的运算符
作用域运算符::
点操作符(访问类对象).
点星操作符(访问成员对象指针).*
条件运算符?:
逻辑运算符具有短路求值的特性,而运算符重载相当于函数调用,这就失去了短路求值的特性。
PS:前置运算符与后置运算符重载
后置为++
前置为++(int)
new 的啥类
delete时候找哪个类
如果析构函数是虚函数则调用其派生类
1、多继承子类会继承所有基类成员变量(protected\public)
2、菱形继承(钻石继承)touchable在tapscroll中有两份,如果多继承层数较多,则子类对象会发生膨胀。解决办法:在中间继承类定义的冒号后面加上virtual
1、抽象基类:像具体动物只有猫狗猪这样的具体对象,没有动物对象,我们不想让Animal生成对象,就要把该基类声明为抽象基类。具体方法就是在里面加上纯虚函数。例如:virtual void yoll()=0;
2、析构函数的调用顺序按照继承关系的反顺序
与构造函数调用顺序正好相反。
3、虚析构函数的作用
Animal* anim = new Dog;
delete anim;
delete anim; 如果基类中的析构函数不是虚函数,则将直接调用基类中的析构函数,不会调用派生类中的析构函数。
1、对齐操作 2m48s(总线宽度限制 32位 4字节)
2、成员变量存放顺序按照声明顺序 5m23s
3、重写(override) 9m15s
4、virtual 虚函数声明
5、子类函数后面加上override
6、运行时决定虚函数到底调用哪个函数
7、如果不用虚函数,子类无法重写父类函数的行为
8、编译时,积累与派生类中每一个yoar()函数都生成唯一的一个标识符
9、对于非虚函数的函数,编译过程中就已知了调用的具体函数内容
10.sizeof() 子类与父类,子类将继承父类的public与protected的成员变量,virtual虚函数会引入一个指针 vptr->vtable