类与对象:
const常量哪怕栈空间数据被改 被访问时依然是使用存于符号表的数据
构造函数的形式:
拷贝构造 Test(Test &t){}
初始化构造 Test(int d): data(d){}
Test t(10);
t = 200;
//只要类型不同,会创建一个中间的无名变量来赋值;
//实际上是调用了构造函数中的 Test(200) 创建了一个对象,将对象赋值给t;
//如果构造函数中使用explicit作为修饰,不能隐式转化时,会杜绝这种情况发生;
//上一行的情况则必须 使用: t = (Test) 200;
Test t1 = t2; //拷贝构造
Test t1; t1 = t2; //运算符重载赋值
拷贝构造: 形如 void fun(Test a);
如果函数的参数是以传入对象的形式, 那么传入对象的时候会进行一次拷贝构造
返回值是对象的时候: Test fun(int a)
{
Test new_one(a);
return new_one;
}
如果编译器做了优化 可能会延长返回对象的生存周期 而不是重新拷贝构造一个无名对象来返回
*拷贝构造的时候要考虑浅拷贝和深拷贝的问题
注意: 局部对象不能以引用形式返回,局部对象会被销毁
*拷贝构造的参数最好使用const修饰 比如Test(const Test &t)(…)
因为return的时候可能会检测到拷贝构造函数,而return返回的对象具有常性,需要const属性
赋值函数的编写:
1.自身赋值问题
2.释放原有内存的空间
3.开辟空间深拷贝赋值数据
4.返回自身对象
—- Tip:异常安全问题 在赋值期间发生异常 保证赋值两者的数据安全
*类成员有指针的时候 考虑拷贝构造和析构还有运算符重载的安全性
vld.h: 检测内存泄露的库
Question: C++的类的初始的函数有六个 分别是什么?
explicit关键字的用法?
如何把一个只有一个数据成员的对象直接给一个该数据的变量赋值?(Test t(200); int value = t;)
成员函数中用const修饰的方法:
void Test::fun()const;
实际上相当于
void Test::fun(const Test * const this);
本质上就是不能修改this所指的对象的值;
***tips: 常方法不能调用有可能改变对象的值的成员函数;
成员函数中的static修饰的方法:
static修饰的变量以及函数,被所有对象共享,没有this指针;
***tips: 静态方法不能调用成员函数,因为它是被所有类对象共享的;
用friend关键字修饰的友元函数可以访问私有成员;
— 常用来重载流运算符;
运算符重载:
**不能使用的重载运算符:**
三目运算符: ?:
成员访问符: . .*
字长访问符: sizeof
作用域符: ::
++运算符的重载:
前置++: Test operator++();
后置++: Test operator++(int);
此处的int仅作为标记使用;
流运算符重载:
friend ostream& operator<<(ostream &out, const Test &t);
{
out << t.data;
return out;
}
作为友元函数进行重载;
C++如何实现的重载:
编译器在汇编阶段根据函数的参数通过自己的一套命名方式
将函数进行一系列重命名,翻译成另一个具有唯一性的名字(带上函数的参数)
当加入extern "C" 修饰函数的时候用C的模式汇编
空间分配(new && delete):
new 等同于 C 语言中的 (强制转化 *)malloc(sizeof(类型));
int *p = new int(10); //新建一个值为10的整形
int *p = new int[10]; //新建一个有10个成员的整形数组
int *p = new Test; //调用构造函数申请空间(如果要求有参数必须写参数)
int *p = new Test[10]; //构造10个对象!
//Tips: 如果要直接构造,一定要有默认(带参)的构造函数
delete []p; //*** 通过delete删除掉连续申请的对象空间 否则内存泄露
//方括号只起到标记作用,标识是释放一个数组空间
new的步骤:
1.申请空间;
2.调用构造函数在申请的空间内构造对象;
delete的步骤:
1.调用析构函数析构对象;
2.释放空间;
模板:
#include <typeinfo>
template<typename Type>
Max(Type a, Type b)
{
cout<<typeid(Type).name()<<endl;
//打印出Type的类型
return a>b ? a:b;
}
实现是先通过一系列操作与函数模板, 制作出相应类型的模板函数
最后再用模板函数来实现函数功能
类的形式类似;
可以直接调用:
Max(1,2);
Max(‘A’, ‘X’);
或者显式调用:
Max(1,2);
Max(‘A’, ‘X’);
Gdb调试:
1.g++ -o -g
2.gdb
3.list
4.b 加断点
5.run
new && delete:
1.new operator -> 1)分配空间 2)构造函数
(直接使用 new 关键字)
2.operator new -> 只分配空间
(直接使用 operator new 关键字)
可以重载new操作符来影响其分配空间的操作
void *operator new(size_t sz)
{
void *p = malloc(sz);
return p;
}
要求返回值void * 传入参数 size_t
同时如果你重载了new操作符,就要求你重载delete操作符
void operator delete(void *p)
{
free(p);
}
*Tips:free 与 malloc对应
new 与 delete对应
operator new 与 operator delete 对应
同理 new[] 与 delete[] 也可以重载
实际上new[] 在申请空间的时候,
头部会多出一部分cookie空间, 大小为一个指针的大小
存储着用户的信息:起始位置,释放多少个对象
此时才可以以delete[]来释放相应的空间
类里面也可以重载new delete操作符,而且会被对象优先使用
如果是void*p = new Test(10);
那么delete p的时候 不会调用类的析构函数;
3.placement new
通过malloc开辟空间之后
int p = (int )malloc(sizeof(int) * 10)
可以通过new(p)int(10)来对申请的空间进行赋值
这种语法称之为定位new
这样的new也是可以重载的:
void *operator new(size_t sz, void *data, int pos)
{
return (data + pos);
}
则使用方法变为
new(p, 3)int(200);
对p所指的第三个位置进行赋值200的操作
继承和多态:
子类虽然公有继承了基类的私有成员
但是依然不能在自己的方法中改变基类
正确的用法是在基类中设置相应的设置方法来设置基类的成员
父类的信息能否被子类调动?
***父类任意私有数据都不能被子类直接调用
但是父类的公有方法与保护方法都可以被子类的方法调用
只有公有方法可以被对象直接访问
保护方法存在的意义:
***保护只在继承的时候体现它的性质
不想被外部的对象调用, 为了给子类提供借口而存在
其他一切特点等同于私有
class C: D
默认是私有继承 (class C: private D)
多继承:
多继承的构造函数顺序就是继承列表从左往右
同时先构造成员的类对象再构造自身类对象
构造函数书写方法:
C(a, b, c): A(a), B(b), C(c){}
子类要调用基类的构造方法
二义性:
父类有同名的变量
那么需要通过加入 父类:: 来避免二义性
虚继承:
可以避免继承同一个基类产生变量的二义性
同名隐藏:
如果子类和父类有同名的方法
那么父类的同名方法会完全被隐藏(哪怕是重载)
但是可以通过使用::这个来直接调用父类方法
看书:
1.重载 2.覆盖 3.隐藏