文章目录
函数后面加const
加了cosnt的成员函数,表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中,任意修改它所在的类的成员的操作都是不允许的(因为隐含了对this指针的const引用)
=delete
-
C++11引入的
=delete
是一种特性,它用于明确禁用或删除类的成员函数、特殊成员函数、或者其他成员函数。=delete
的主要目的是在编译时捕获潜在的错误,并提供更精确的控制,以确保类的行为符合设计要求-
禁用默认函数
class NonCopyable { public: NonCopyable() = default; // 使用=delete禁止复制构造函数和复制赋值运算符 NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; };
-
防止意外重载
#include <iostream> void myFunction(int a) { std::cout<<a<<std::endl; } void myFunction(double) = delete; // 使用=delete禁止double类型的重载 int main() { myFunction(42); // 调用int版本 // myFunction(3.14); // 无法编译,double版本已被删除 return 0; }
-
多线程
std::mutext mutext;
std::unique_lock<std::mutex> lock(mutext);
std::lock_guard<std::mutext> lock1(mutext);
const
- c中 const 全局只读变量,分配内存
- c++中,是否为 const 常量分配内存空间依赖于如何使用。一般说来,如果一个const 仅仅用来把一个名字用一个值代替(就像使用#define 一样),那么该存储局空间就不必创建
- 在 c++中,出现在所有函数之外的 const 作用于整个文件
构造函数
void test(){
Person p; //无参构造
Person p1(10);//1. 常用
Person p2 = Person(10);//2显式法
Person p3 = Person(p2);
Person p4 = 10; // Person p4 = Person(10);// 3.隐式转换
Person p5 = p4; // Person p5 = Person(p4);
}
- 拷贝构造函数调用时机
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象 ??
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
移动构造函数
- 当类中拥有指针类型的成员变量时,拷贝构造函数中需要以深拷贝(而非浅拷贝)的方式复制该指针成员
- 移动构造函数 c++11 将其他对象(通常是临时对象)拥有的内存资源“移为已用”
- 右值引用的符号是&&
右值引用 ??
int main(){
// 左值引用只能引用左值,不能引用右值。
int a = 10;
int& ra1 = a; // ra1为a的别名
//int& ra2 = 10; // 编译失败,因为10是右值
// const左值引用既可引用左值,也可引用右值。
const int& ra3 = 10;
const int& ra4 = a;
//右值引用只能右值,不能引用左值。
int&& r1 = 10;
int a = 10;
//message : 无法将左值绑定到右值引用
int&& r2 = a;
//右值引用可以引用move以后的左值
int&& r3 = std::move(a);
return 0;
}
- 左值引用的意义
- 函数传参:实参传给形参,可以减少拷贝
- 函数传返回值时候,只要是出了作用域还存在的对象,就减少拷贝
- 右值引用的应用
- C++中进行对象赋值时,有时候会发生对象的深拷贝,如果对象的堆内存太大,拷贝的代价是很大的,所以程序性能会降低,如果想要避免深拷贝,可以使用右值引用
输入型参数 输出型参数
形参列表中的参数:
- 普通变量一定是输入型参数 ,普通变量传递向函数外部的方式只有两种:
- 全局变量,风险大,可读性差一般不用。
- 返回值,外部需要用新开的空间对接
- 指针变量
- 内部不会进行修改,一般加const
- 作为输出,指针型,且没有const
内联函数
- 经常把短而执行频繁的计算写成宏,而不是函数,
- 好处:为了执行效率,宏可以避免函数调用的开销,这些都有预处理来完成,
- 坏处:
- 宏会有一些难以发现的错误 c/c++都有
- 预处理器不允许访问类的成员,
- 为了保持预处理的效率有增加安全性,而且能像一般成员函数那样访问类成员,引入内联函数
#define ADD(x,y) x+y
inline int Add( (int x, ,int y ){
return x + + y; ;
}
void test (){
int ret1 = = ADD( (10, 20) ) * 10; ; //希望的结果是 300
int ret2 = = Add( (10, 20) ) * 10; ; //希望结果也是 300
cout << "ret1:" << ret1 << endl; ; //210
cout << "ret2:" << ret2 << endl; ; //300
}
智能指针
智能指针的原理就是,将一个申请好的内存地址保存在智能指针结构体内部,然后把智能指针保存在栈上。当智能指针出了作用域后,由于栈上的变量会自动销毁,所以之前传入保存在智能指针内部的内存块的析构函数也会被相应调用。智能指针和普通指针最好不要一块使用,否则很容易造成使用同一个对象初始化了多个智能指针而导致对象被多次析构的情况。只有第一个智能指针使用普通指针来初始化,后续和此普通指针相关的智能指针都应该使用智能指针来初始化。
-
shared_ptr
- shared_ptr多个指针指向相同的对象,使用引用计数来完成自动析构的功能。
- shared_ptr的引用计数是线程安全的,但是其对象的写操作在多线程环境下需要加锁实现。
- 不要用同一个指针初始化多个shared_ptr,这样可能会造成二次释放。
虚函数
- 定义一个函数为虚函数,不代表函数为不被实现的函数。
- 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
- 定义一个函数为纯虚函数,才代表函数没有被实现。virtual void funtion1()=0;
- 一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为"虚"函数。虚函数只能借助于指针或者引用来达到多态的效果。
深拷贝
类中有指针,并且指针有动态分配空间,析构函数做了释放处理,
就需要自定义拷贝构造函数,自行给指针动态分配空间
否则对象回收的时候,会调用析构函数,回收两次,出现错误。
c++ 继承
- public 继承,子类可访问父类 public protected ,不可访问private,其他类只能访问子类puclic
- protect 继承,可访问父类public protected,不可访问private, 其他类不可访问子类中的
- private 继承,可访问父类public protected,不可访问private,其他类不可访问,孙类也无法访问所有
- 调用顺序 ,父类构造,子类构造,子类析构,父类析构
- 如果父子类有同名成员变量和函数,访问父类成员需要加作用域 son.Base::func()
- 菱形继承:某个类同事继承两个派生类
- 菱形继承主要问题是子类继承两份相同数据,导致资源浪费且毫无意义
- 可用虚继承解决菱形继承问题
using
-
使得指定的标识符可用
-
using 编译指令使整个命名空间标识符可用
-
指定别名
-
改变派生类对父类成员的访问控制
namespace A{ int paramA = 20; int paramB = 30; void funcA(){ cout << "hello funcA" << endl; } void funcB(){ cout << "hello funcA" << endl; } } void test(){ //通过命名空间域运算符 cout << A::paramA << endl; A::funcA(); //1. using 声明 using A::paramA; using A::funcA;//如果是重载函数,则全部可用 cout << paramA << endl; //cout << paramB << endl; //不可直接访问 funcA(); //同名冲突 //int paramA = 20; //相同作用域注意同名冲突 //释放整个命名空间 using namespace A; } //====2.改变对派生类的控制============ class T5Base { public: T5Base() :value(55) {} virtual ~T5Base() {} void test1() { cout << "T5Base test1..." << endl; } protected: int value; }; class T5Derived : private T5Base { public: //using T5Base::test1; //using T5Base::value; //基类中成员变量 value 是protected,在 private 继承之后,对于外界这个值为 private,也就是说T5Derived 的对象无法使用这个 value。 //如果想要通过对象使用,需要在public下通过 using T5Base::value 来引用,这样 T5Derived 的对象就可以直接使用。 void test2() { cout << "value is " << value << endl; } }; //4. 指定别名 typedef void (*FP) (int, const std::string&); //函数指针 //c++ 11中 using 的写法把别名的名字强制分离到了左边,而把别名指向的放在了右边,比较清晰 using FP = void (*) (int, const std::string&); //比typeef 可读性好 typedef std::string (Foo::* fooMemFnPtr) (const std::string&); using fooMemFnPtr = std::string (Foo::*) (const std::string&); //using 可以做到模板别名,typedef做不到 template <typename T> using Vec = MyVector<T, MyAlloc<T>>; Vec<int> vec;// usage
std::function 函数包装模板
#include<iostream>
#include <functional>
using namespace std;
// c type global function
int c_func(int a, int b)
{
return a + b;
}
int main()
{
typedef int(*Func)(int ,int);
Func f1 = c_func;
cout<< f1(1,2)<<endl; //3
std::function<int(int, int)>f2 = c_func;
cout<<f2(1, 2)<<endl; // 3
system("pause");
return 0;
}
可以调用:
- 普通函数
- 函数对象 仿函数
- lambda
- 静态成员函数
- 类成员函数
- 类公有数据成员
- binder