c++学习
(01)使用传统的有参构造函数,在构造函数内部进行变脸过的赋值;
(02)使用构造函数初始化列表进行初始化:构造函数后面+(:属性(参数),属性(参数)… ;
//需求:创建类中的对象并且保证只有一个这样的对象实例
class printer {
private:
printer() {}
printer(const printer &p);
public:
static printer * get_instance() {
return single_printer;
}
private:
static printer* single_printer;
};
printer *printer::single_printer = new printer;
void test01() {
//拿到打印机
printer * printer_ = printer::get_instance();
}
ostream & operator<<(ostream &cout, my_string &str) {
cout << str.p_string;
return cout;
}
//声明
template<class nametype, class agetype >class person;
template<class nametype, class agetype>void do_work(person<nametype, agetype> &p);
父类和子类之间的转换:向下转换不安全,向上转换安全;(父类所维护的空间比子类小)
父子转子不可以,子转父可以,在发生多态的情况下都可以转换;
- 常量转换:只对指针或者引用有效,
- 动态类型转化: 基础数据类型不可以转换;
- 静态类型转换:基础数据类型:使用static_cast<要转换的目标类型>(要转换的数据);
- 多态就是父类的指针或者引用指向子类;
- 菱形继承:解决父类身上的多个同属性成员的二义性继承和空间浪费,使用虚基类(虚基类指针和虚基表,通过表找到偏移量,找到共享数据);
- 动态联编:如果是全局函数传参为继承中的父类或者子类,此函数会静态联编(在编译时期进行早绷定,也无法使用作用域进行区分),需要把函数重调用的类中的方法声明为虚函数,进行晚绷定即在运行调用时选择要传入的参数;(即父类的引用或指针 指向子类时使用多态);
- 多态有利于后期的扩展,可读性高,缺点是效率低一点;
- 父类中有纯虚函数,那么父类无法实例化对象,子类继承父类必须要实现纯虚函数;
- 虚析构用来解决父类指针指向子类时,释放空间不彻底的问题
- 纯虚析构函数,需要在类内声明,类外实现,包含纯虚析构函数的类是抽象类;
- 普通函数可以进行隐式类型转换,函数模板不可以;
- c++编译器优先考虑普通函数;可以通过空模板实参列表<空参数>的方式限定编译器只能通过模板匹配;函数模板可以像普通函数一样进行重载;
- 模板不能解决所有的类型;可以通过具体化来解决:template<>返回值 函数名<具体类型>(参数)
- 类模板不是类名不确定,而是类中的成员的类型不确定;
- 类模板并不能进行自动类型推导,需要进行显式的指定类型;
- 类模板可以指定默认的类型参数:class T = int;
- 子类继承父模板类,必须指定出父模板类的类型,否则,子类无法分配内存;
- 模板类中友元函数的类外实现需要时,01)类中的友元声明中需要加入<>空参数列表到声明中,如:friend void do_work<>(person<nametype, agetype> &p);//只是普通的函数的声明,需要使用空参列表强制使用模板函数 02)需要在整个类前声明类和友元函数;如:
- 子类会继承父类的私有成员,只是被编译器隐藏,无法访问;
- 构造函数调用顺序:子类——》父类,析构函数调用正好相反,并且子类不会继承父类中的构造和析构函数;
- 如果父类中没有默认构造函数,子类可以通过初始化列表显示的调用父类的其他构造函数;
- 继承中同名函数的调用默认使用就近原则,除非使用如:son.base::L_value这种方式显示的区分调用父类中的同名函数;也就是加上父类的作用域;
- 静态成员函数:可以通过类名(作用域)直接访问公有静态成员函数,也可以通过对象访问公有静态成员函数;
- 只有非静态成员变量才属于对象;
- 成员变量和成员属性是分开存储的;
- 空类的大小为1,使用char维护类地址;
- this指针指向被调用的成员函数所属的对象;
- 如果成员函数没有用到this,那么空指针可以直接访问到,如果成员函数用到this指针,要注意判断对象指针是否为空;
- 常函数 void func() const{},修饰的是this指针 ,const type* const this;,不能修改this指针指向的值;
- 常对象,在对象前加入const修饰对象,不可以调用普通的成员函数,能调用常函数;
- 使用(.hpp)调用外部模板文件;
- 对于+运算符的重载,使用(operator+)进行重载;
- 使用全局函数实现+运算符的重载,需要手动实现运算符重载函数的内容;在使用时直接person p2 = p1+p3;
- 对于运算符的重载:运算符重载只能重载基本的数据类型,对于自定义的数据类型,需要手动实现重载函数;
- 左移运算符的重载只能使用全局函数定义;void operator<<(参数一是ostream;参数二是要打印的数据类型);
- 类对象也可作为成员;
- explicit关键字防止隐式类型转换;
- 建立类对象数组:Person * pArray = new Person[10];通过new(也就是堆区)开辟数组一定会调用构造函数;所以一点更要提供默认构造;使用delete[]person;释放;
- 静态成员变量会共享数据,在类内声明,类外实现;
- 静态成员函数可以在类内实现,不可以访问普通的成员变量(静态成员函数无法区分是哪个对象的成员) ,可以访问静态成员变量(静态成员变量是共享的,只有一份);
- 单例模式:一个类只可以实例化出一个对象;
- 单例模式原理:利用私有static静态成员变量的的唯一性,定义一个私有的静态成员指针指向内部实现的对象;在提供一个接口在类内部返回这个私有的指向对象的指针;详细如下:
- 当一个函数体的内部需要改变实参的值时,则需要使用指针参数。qq.com
- 函数调用时实参值将复制到形参中。
- 当const出现在*号左边时,指针指向的数据为常量
- 当const出现在*号的右边是时,指针本身为常量
- c++ new 出来的对象需要用指针指向
- 子类从父类继承过来的函数可以按照需求重写
- 父类指针可以指向子类 如:perent *p = new child(); ()是用来给构造函数传参的
- inline函数,为了解决简单而又频繁调用的函数反复的压栈出栈的操作冗余,相当于宏函数,省去了压栈出栈的操作
- 函数指针定义方法之第三种:int myfucn(){/*/*/*/*/*/},int"函数返回类型"(*pointer_name)(“函数的形参”) = NULL;pointer_name = myfunc;
- 函数指针不能发生重载,因为指针指向的函数地址不同。
- 对于拷贝构造函数,如果不写会自动调用默认的拷贝构造函数,如果写可以自定义拷贝构造函数
- 构造函数是对象初始化的时候调用的
- 当自己提供了有参构造函数,系统不会再提供默认无参构造函数,但依然会提供拷贝构造函数;
- 当自己提供了拷贝构造函数,系统不会再提供其他构造函数;
- 拷贝构造函数形如:test(const test &another){},使用const避免拷贝过程中修改类名;
- 在对含有指针成员的对象进行拷贝时,必须要自定义拷贝构造函数,使拷贝后的指针成员有自己的内存空间,即进行(深拷贝),如果使用默认拷贝构造函数,则两个指针会指向同一个对象内存空间,析构时要析构两次,会发生内存溢出。
- 类中的const常量必须在初始化列表中进行初始,不能使用赋值的方法进行初始化
- frend友元函数(友元类),允许函数(友元类)使用类的私有成员。
- 函数的重载就是简单的静态多态。
- 一个基类中如果包含一个或者多个虚函数,就是抽象基类,抽象基类不能也没必要定义对象。
- 抽象基类体现了本类族中的共性,把各类中 共有的成员函数集中在抽象基类中声明。
- 操作符的重载有两种方式,即友元函数或者类成员函数。
- C++中使用<fstream>头文件来操作文件。Fstream read("路径");先声明一个ifstream对象,不进行初始化,然后将对象和open()关联起来。
- 命令行技术:int main(int argv,char*argv[]){} ,其中int argc 是参数的个数,而char*argv[]是参数。
- 头文件<cctype> 中的toupper实现小写转换为大写字母。
- 库就是二进制文件。使用需要头文件和制作出来的库。
- c语言中const修饰的是伪常量,编译器会给分配内存。c++中const定义的常量使用指针也不可修改;
- 引用起别名:int &b = a;引用必须初始化,且不可修改;给数组其别名:int(&parr)[]=arr;
- 引用的实质就是指针常量;
- 函数重载的原理其实是编译器内部进行自动改名;
- c语言中没有函数重载,如果C++想调用c语言的函数,01直接在cpp中需要使用extern+函数名;进行声明;02或者在c文件中使用#ifdef。。。。进行声明;
- 构造函数与类名相同,没有返回值(注意是没有返回值,而不是void返回值)(可以有参数);
- 调用默认无参构造函数不需要加()括号;
- 对于浅拷贝来说,如果属性中有指向堆区的数据,只是简单的拷贝了数据的内存地址,但是释放的时候却释放两次,程序崩溃,解决的方法是进行深拷贝,手动定义拷贝构造函数,在拷贝构造函数中重新申请新的内存空间,再把元数据赋值到新空间中;
- l_name = (char*)malloc(strlen(p.l_name) + 1);之所以加1是因为字符串最后还有(\0);strlen()计算的是字符串的有效长度;
- 类初始化数据有两种方法:
- (01)全局变量的检测;
- (02)函数检测的增强;参数类型增强,返回值检测增强,函数调用的参数检测增强;
- (03)类型转换增强;
- (04)struct增强;c语言中struct成员中不可以有函数;c++中可以添加;c语言中实现结构体必须加struct关键字,c++中可以不加struct;
- (05)c++中增加bool类型;
- (06)三目运算符增强;c语言中返回的是值;c++中返回的值变量,可以直接重新赋值;
- c++对c语言的增强: