2102 字
11 分钟
C++学习0628
2023-06-28

1.noexcept#

用途,两种形式写法与功能

1

1

1

noexcept表示其修饰的函数不会抛出异常 。

double divisionMethod(int a, int b) noexcept
{
if (b == 0)
{
cout << "division by zero!!!" << endl;
return -1;
}
return a / b;
}

从语法上讲,noexcept 修饰符有两种形式:

​ ● 简单地在函数声明后加上 noexcept 关键字

​ ● 可以接受一个常量表达式作为参数,如下所示∶

double divisionMethod(int a, int b) noexcept(常量表达式);
void fun(int a)noexcept(0)//值为 false,表示有可能抛出异常这里
{
if (a == 0) throw("抛出异常");
}
void fun1(int a)noexcept("0")//值为 true,表示函数不会抛出异常
{
if (a == 0) throw("抛出异常");
}

2.断言assert#

静态断言#

作用,写法,一个注意

1

1

1

非运行状态下使用,不需要头文件,静态断言会保证第一个参数正确,错误就会报错第二个参数

例如,如果我们想知道当前是32 位还是64位平台,我们可以使用静态断言。static_assert

编译时就能进行检查的断言,使用时不需要引用头文件。

【注意】静态断言的表达式是在编译阶段进行检测,所以它的表达式中不能出现变量,必须是常量表达式。

int main()
{
//如果我们的环境是32位平台那么在编译阶段就会提示我们自己定义的错误 no 32
static_assert(sizeof(int*) == 4, "no 32 "); // 正确的
static_assert(sizeof(int*) == 8, "no 64");//报错 no64
//错误,a是变量,表达式要求是常量表达式
int a = 4;
static_assert(a == 4, "no 32 ");
return 0;
}

动态断言#

作用,如何使用

1

1

1

要引入头文件 #include或者#include<assert.h>

用于帮助锁定错误,对assert()小括号里的语句进行判断,如果错误就断到这了,并提示问题出现再哪个文件中

// 创建一个指定大小的 char 类型数组
char* createArray(int size)
{
// 通过断言判断数组大小是否大于0
assert(size > 0); // 必须大于0, 否则程序中断
char* array = new char[size];
return array;
}
int main()
{
char* buf = createArray(0);
return 0;
}

3.final关键字#

作用,写法

1

1

1

限制类不能被继承、虚函数不能被重写,修饰函数只能修饰虚函数

c++增加了final 关键字来限制某个类不能被继承或者某个虚函数不能被重写,如果final修饰函数只能修饰虚函数,并且要把final关键字放到类或者函数的后面。

修饰函数#

如果使用final修饰函数,只能修饰虚函数,这样就可以防止子类重写父类这个函数

【扩展】如果基类的虚函数使用final修饰,那么子类无法重写这个虚函数,子类也是抽象类,后面继承这个类的都没法重写这个函数都是抽象类

class Base
{
public:
virtual void test()
{
cout << "Base class...";
}
};
class Child : public Base
{
public:
void test() final
{
cout << "Child class...";
}
};
class GrandChild : public Child
{
public:
// 语法错误, 不允许重写
void test()
{
cout << "GrandChild class...";
}
};

修饰类#

使用final关键字修饰过得类不允许被继承,也就是说这个类不能有子类

class Base
{
public:
virtual void test()
{
cout << "Base class...";
}
};
class Child final : public Base
{
public:
void test()
{
cout << "Child class...";
}
};
// 语法错误
class GrandChild : public Child
{
public:
};

4.override#

作用,写法

1

1

1

override关键字明确的表明将会重写父类的虚函数

和final的用法相同,放在函数后面。提高了程序的正确性,降低了出错概率。

class Base
{
public:
virtual void test()
{
cout << "Base class...";
}
};
class Child : public Base
{
public:
//正确,重写了父类的虚函数
void test() override
{
cout << "Child class...";
}
};
class GrandChild : public Child
{
public:
//报错,父类中没有相同的虚函数可以被重写
void test(int a) override
{
cout << "Child class...";
}
};

5.=default#

作用(类内,类外),不能用default修饰的,一个注意

1

1

1

  • 在类内修饰满足条件的类函数为显示默认函数
  • 在类外修饰类的成员函数为默认函数
class Base
{
public:
//指定无参构造为默认函数
Base() = default;
//指定拷贝构造函数为默认函数
Base(const Base& obj) = default;
//指定移动构造函数为默认函数
Base(Base&& obj) = default;
//指定复制赋值操作符重载函数为默认函数
Base& operator= (const Base& obj) = default;
//指定移动赋值操作符重载函数为默认函数
Base& operator= (Base&& obj) = default;
//指定析构函数为默认函数
~Base() = default;
};

默认函数除了在类定义的内部指定,也可以在类的外部指定。

// 类定义
class Base
{
public:
Base();
Base(const Base& obj);
Base(Base&& obj);
Base& operator= (const Base& obj);
Base& operator= (Base&& obj);
~Base();
};
// 在类定义之外指定成员函数为默认函数
Base::Base() = default;
Base::Base(const Base& obj) = default;
Base::Base(Base&& obj) = default;
Base& Base::operator= (const Base& obj) = default;
Base& Base::operator= (Base&& obj) = default;
Base::~Base() = default;

不能用=default修饰的#

自定义带参构造

自定义函数

【注意】不是移动、赋值运算符重载的不能用=default修饰(算自定义函数)

class Base
{
public:
Base() = default;
Base(const Base& obj) = default;
Base(Base&& obj) = default;
Base& operator= (const Base& obj) = default;
Base& operator= (Base&& obj) = default;
~Base() = default;
// 以下写法全部都是错误的
Base(int a = 0) = default; //自定义带参构造,不允许使用 =default 修饰(即使有默认参数也不行)
Base(int a, int b) = default; //自定义带参构造,不允许使用 =default 修饰
void print() = default; //自定义函数,不允许使用 =default 修饰
//下面两行不是移动、复制赋值运算符重载,不允许使用 =default 修饰
bool operator== (const Base& obj) = default;
bool operator>=(const Base& obj) = default;
};

后两行vs报错如下:

6.=delete#

作用,禁用两个

1

1

1

显示删除,避免用户使用不应该使用的类的成员函数

禁止使用默认生成的函数#

class Base
{
public:
Base() = default;
Base(const Base& obj) = delete; //禁用拷贝构造函数
Base& operator= (const Base& obj) = delete; //禁用 = 进行对象复制/
};
int main()
{
Base b;
Base tmp1(b); // 报错 拷贝构造函数已被显示删除,无法拷贝对象
Base tmp = b; // 报错
return 0;
}

禁止使用自定义的函数#

防止参数隐式转换

class Base
{
public:
Base(int num) : m_num(num) {}
Base(char c) = delete; //禁用带 char 类型参数的构造函数,防止隐式类型转换(char 转 int)
void print(char c) = delete; //禁止使用带 char 类型的自定义函数,防止隐式类型转换(char 转 int)
void print()
{
cout << "num: " << m_num << endl;
}
void print(int num)
{
cout << "num: " << num << endl;
}
private:
int m_num;
};
int main()
{
Base b(97);
Base b1('a'); // 'a' 对应的 acscii 值为97 报错 对应的构造函数被禁用,因此无法使用该构造函数构造对象
b.print();
b.print(97);
b.print('a'); // 报错 对应的打印函数被禁用,因此无法给函数传递 char 类型参数
return 0;
}

7.委托构造#

概念,两点

1

1

1

委托构造函数允许使用同一个类中的一个构造函数调用其他的构造函数,从而简化相关变量的初始化。

class Test
{
public:
int min;
int mid;
int max;
Test(int min)
{
this->min = min;
}
Test(int min, int max):Test(min)
{
this->max = max;
}
Test(int min, int mid, int max):Test(min,max)
{
this->mid = mid;
}
};
int main()
{
Test t(1,2,3);
cout << t.min << " " << t.mid << " " << t.max << endl;
return 0;
}

​ ● 链式的调用委托构造不能形成一个闭环。

​ ● 在初始化列表调用了委托构造,就不能再初始化列表中初始化其他变量了

复习内容#

双引号代表地址常量,字符串“hello world!”是()#

1

1

1

const char*类型

sizeof与strlen#

区别

1

1

1

sizeof是运算符

strlen是库函数,以\0结束,不算\0

malloc和new#

区分,申请失败时会发生什么

1

1

1

new是操作符,malloc是函数

malloc传一个负数,会申请内存失败并返回一个void*

new申请失败返回一个异常

拷贝构造和赋值#

区分

1

1

1

赋值是已有对象,拷贝构造是创建新对象,区分通过是否有新对象

__中void*不能隐式转换#

1

1

1

C++11中void*不能隐式转换,c中可以

C++学习0628
https://fuwari.cbba.top/posts/c学习0628/
作者
Chen_Feng
发布于
2023-06-28
许可协议
CC BY-NC-SA 4.0