智能指针的作用是管理一个指针,避免程序员申请的空间在函数结束时忘记释放,造成内存泄漏的情况发生。智能指针是一个类,当超出了类的作用域,类会自动调用析构函数 析构函数详解,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时,自动释放内存空间,不需要手动释放内存空间。
常用接口:
T* get();
T& operator*();
T* operator->();
T* operator = (const T& val);
T* release();
void reset(T* ptr = nullptr);
1、auto_ptr (c++98的方案,c11已抛弃)采用所有权模式;
auto_ptr<std::string> p1 (new string ("hello"));
auto_ptr<std::string> p2;
p2=p1;//auto_ptr不会报错
此时不会报错,P2剥夺了P1的所有权,但当程序运行时,访问p1会报错,所以auto_ptr的缺点是:存在潜在的内存崩溃问题。
2、unique_ptr(替换auto_ptr)
unique_ptr 实现独占式拥有或严格拥有概念,保证同⼀时间内只有⼀个智能指针可以指向该对象。它对于避免资源泄露特别有⽤。采⽤所有权模式,还是上⾯那个例⼦
unique_ptr<string> p3(new string(auto));//#4
unique_ptr<string> p4;//#5
p4 = p3//此时会报错
编译器认为 p4=p3 ⾮法,避免了 p3 不再指向有效数据的问题。因此, unique_ptr ⽐ auto_ptr 更安全。
3、shared_ptr(共享型,强引用)
4、waek_ptr(弱引用)
static 作⽤:控制变量的存储⽅式和可⻅性。
const 关键字:含义及实现机制
首先,C和C++在基本语句上基本没有多大区别。
C++有新增的语法和关键字,语法的区别有头文件的不同和命名空间不同,C++ 允许我们⾃⼰定义⾃⼰的空间, C 中不可以。关键字⽅⾯⽐如 C++ 与 C 动态管理内存的⽅式不同,C++ 中在 malloc 和 free 的基础上增加了 new 和 delete,⽽且 C++ 中在指针的基础上增加了引⽤的概念,关键字例如 C++中还增加了 auto, explicit 体现显示和隐式转换上的概念要求,还有 dynamic_cast 增加类型安全⽅⾯的内容。
函数⽅⾯ C++ 中有重载和虚函数的概念: C++ ⽀持函数重载⽽ C 不⽀持,是因为 C++ 函数的名字修饰与 C 不同, C++ 函数名字的修饰会将参数加在后⾯,例如, int func(int,double)经过名字修饰之后会成_func_int_double,⽽ C 中则会变成 _func,所以 C++ 中会⽀持不同参数调⽤不同函数。
C++ 还有虚函数概念,⽤以实现多态。
类⽅⾯, C 的 struct 和 C++ 的类也有很⼤不同: C++ 中的 struct 不仅可以有成员变量还可以成员函数,⽽且对于 struct 增加了权限访问的概念, struct 的默认成员访问权限和默认继承权限都是 public, C++ 中除了 struct 还有 class 表示类, struct 和 class 还有⼀点不同在于 class 的默认成员访问权限和默认继承权限都是 private。
C++ 中增加了模板还重⽤代码,提供了更加强⼤的 STL 标准库。
最后补充⼀点就是 C 是⼀种结构化的语⾔,重点在于算法和数据结构。 C 程序的设计⾸先考虑的是如何通过⼀个代码,⼀个过程对输⼊进⾏运算处理输出。⽽ C++ ⾸先考虑的是如何构造⼀个对象模型,让这个模型能够契合与之对应的问题领域,这样就能通过获取对象的状态信息得到输出。
C 的 struct 更适合看成是⼀个数据结构的实现体,⽽ C++ 的 class 更适合看成是⼀个对象的实现体。
对于局部常量,存放在栈区:
对于全局常量,编译期一般不分配内存,放在符号表中,以提高访问效率;
字面值常量,比如字符串,放在常量区。
重载
翻译⾃ overload,是指同⼀可访问区内被声明的⼏个具有不同参数列表的同名函数,依赖于C++函数名字的修饰会将参数加在后⾯,可以是参数类型,个数,顺序的不同。根据参数列表决定调⽤哪个函数,重载不关⼼函数的返回类型。
C++中的重载和java中的差不多,都是在一个类中,方法名相同而参数不同的几个方法,但是不能靠返回类型来判断。例如:
class AA{
public:
void print(){
cout << "记得点赞,欢迎交流" << endl;
}
void print(int x){
cout << "共同学习" << endl;
}
};
重写
翻译⾃ override,派⽣类中重新定义⽗类中除了函数体外完全相同的虚函数,注意被重写的函数不能是 static 的,⼀定要是虚函数,且其他⼀定要完全相同。要注意,重写和被重写的函数是在不同的类当中的,重写函数的访问修饰符是可以不同的,尽管 virtual 中是 private 的,派⽣类中重写可以改为 public。
函数特征相同。但是具体实现不同,主要是在继承关系中出现的 。当我们对别人提供好的类的方法感觉不是太满意时,我们就可以通过继承这个类然后重写其方法改成我们需要的逻辑。
1、最重要的一点,重写是子类与父类之间的。
2、被重写的函数不能是 static 的。
3、函数三要素(函数名、函数参数、函数返回类型)完全一样
4、如果父类中有virtual关键字,这种父子之间的关系叫做虚函数重写,这也就是C++中的多态机制,和java自动转换不同的是,C++需要我们手动设置。
class AA
{
public:
virtual void print(int x )
{
cout << "父类:" << x << endl;
}
};
class BB : public AA
{
public:
virtual void print(int x)
{
cout << "子类:" << x << endl;
}
};
int main()
{
AA *p = NULL;
BB b;
p = &b;
p->print(1);
}
这个输出结果取决于AA类中的void print(int x )函数前面是否加入virtual关键字,如果加了则调用子类的,否则父类
重定义(隐藏)
派⽣类重新定义⽗类中相同名字的⾮ virtual 函数,参数列表
和返回类型都可以不同,即⽗类中除了定义成 virtual 且完全相同的同名函数才
不会被派⽣类中的同名函数所隐藏(重定义)。
class AA{
public:
void print(){
cout << "父类,记得点赞,欢迎交流" << endl;
}
};
class BB : public AA
{
public:
void print(int x)//这叫重定义,此时A类中的print()被隐藏
{
cout << "子类:" << x << endl;
}
};
void main()
{
int x = 1;
BB b; //子类
AA a; //父类
a.print();//访问父类的print()
b.print(x);//访问子类的print()
b.AA::print();//访问父类的print()
//b.print();error:函数参数太少
}
这种情况下print()和print(int x)叫作重定义,在重定义时,父类的print()方法被隐藏了,要想使用父类的方法必须通过::
类的对象被创建时,编译系统为对象分配内存空间,并⾃动调⽤构造函数,由构造函数完成成员的初始化⼯作。
即构造函数的作⽤:初始化对象的数据成员。
⽆参数构造函数: 即默认构造函数,如果没有明确写出⽆参数构造函数,编译器会⾃动⽣成默认的⽆参数构造函数,函数为空,什么也不做,如果不想使⽤⾃动⽣成的⽆参构造函数,必需要⾃⼰显示写出⼀个⽆参构造函数。
⼀般构造函数: 也称重载构造函数,⼀般构造函数可以有各种参数形式,⼀个类可以有多个⼀般构造函数,前提是参数的个数或者类型不同,创建对象时根据传⼊参数不同调⽤不同的构造函数。
拷⻉构造函数: 拷⻉构造函数的函数参数为对象本身的引⽤,⽤于根据⼀个已存在的对象复制出⼀个新的该类的对象,⼀般在函数中会将已存在的对象的数据成员的值⼀⼀复制到新创建的对象中。如果没有显示的写拷⻉构造函数,则系统会默认创建⼀个拷⻉构造函数,但当类中有指针成员时,最好不要使⽤编译器提供的默认的拷⻉构造函数,最好⾃⼰定义并且在函数中执⾏深拷⻉。
类型转换构造函数: 根据⼀个指定类型的对象创建⼀个本类的对象,也可以算是⼀般构造函数的⼀种,这⾥提出来,是想说有的时候不允许默认转换的话,要记得将其声明为 explict 的,来阻⽌⼀些隐式转换的发⽣。
赋值运算符的重载: 注意,这个类似拷⻉构造函数,将=右边的本类对象的值复制给=左边的对象,它不属于构造函数,=左右两边的对象必需已经被创建。如果没有显示的写赋值运算符的重载,系统也会⽣成默认的赋值运算符,做⼀些基本的拷⻉⼯作。
区分
A a1, A a2; a1 = a2;//调用赋值运算符
A a3 = a1 ;//调用拷贝构造函数,因为进行的是初始化工作,a3并为存在
C++ 的四种强制转换包括: static_cast, dynamic_cast, const_cast, reinterpret_cast
指针和引⽤都是⼀种内存地址的概念,区别呢,指针是⼀个实体,引⽤只是⼀个别名。在程序编译的时候,将指针和引⽤添加到符号表中。指针它指向⼀块内存,指针的内容是所指向的内存的地址,在编译的时候,则是将“指针变量名-指针变量的地址”添加到符号表中,所以说,指针包含的内容是可以改变的,允许拷⻉和赋值,有 const 和⾮ const 区别,甚⾄可以为空, sizeof 指针得到的是指针类型的⼤⼩。
⽽对于引⽤来说,它只是⼀块内存的别名,在添加到符号表的时候,是将"引⽤变量名-引⽤对象的地址"添加到符号表中,符号表⼀经完成不能改变,所以引⽤必须⽽且只能在定义时被绑定到⼀块内存上,后续不能更改,也不能为空,也没有 const 和⾮ const 区别。
sizeof 引⽤得到代表对象的⼤⼩。⽽ sizeof 指针得到的是指针本身的⼤⼩。另外在参数传递中,指针需要被解引⽤后才可以对对象进⾏操作,⽽直接对引⽤进⾏的修改会直接作⽤到引⽤对象上。
作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引⽤的实质是传地址,传递的是变量的地址