文章目录
auto
auto(自动,automatic)是存储类型标识符,表明变量"自动"具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型
bool
bool(布尔)类型,C++ 中的基本数据结构,其值可选为 true(真)或者 false(假)。C++ 中的 bool 类型可以和 int 混用,具体来说就是 0 代表 false,非 0 代表 true。bool 类型常用于条件判断和函数返回值。
const(运行期常量)和mutable
const(常量的,constant)所修饰的对象或变量不能被改变,修饰函数时,该函数不能改变在该函数外面声明的变量也不能调用任何非const函数。在函数的声明与定义时都要加上const,放在函数参数列表的最后一个括号后。在 C++ 中,用 const 声明一个变量,意味着该变量就是一个带类型的常量,可以代替 #define,且比 #define 多一个类型信息,且它执行内链接,可放在头文件中声明;但在 C 中,其声明则必须放在源文件(即 .C 文件)中,在 C 中 const 声明一个变量,除了不能改变其值外,它仍是一具变量。
mutable(易变的)是 C++ 中一个不常用的关键字。只能用于类的非静态和非常量数据成员。由于一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,对像的状态也会随之发生变化。如果一个类的成员函数被声明为 const 类型,表示该函数不会改变对象的状态,也就是该函数不会修改类的非静态数据成员。但是有些时候需要在该类函数中对类的数据成员进行赋值,这个时候就需要用到 mutable 关键字。
1.普通的成员方法内部可能会改变成员属性, 而在普通的成员方法内部可能会改变成员属性, 而常量是不可改的, 因此常量不能访问普通成员方法, 解决方法是将普通成员方法变成const成员方法.
#include <iostream>
using namespace std;
class A {
public :
A(int x) {
cout << "class A's constructor" << endl;
}
};
class Point {
public :
Point();
Point(int x, int y);
int x() const;
int y() const;
int x_cnt() const;
void set_x(int x);
void set_y(int y);
static int output_cnt();
~Point();
private :
static int point_cnt;
int __x, __y;
mutable int __x_cnt;
A __a;
};
int Point::point_cnt = 0;
int Point::output_cnt() {
return Point::point_cnt;
}
Point::Point() : __x_cnt(0), __a(2) {
Point::point_cnt += 1;
}
Point::Point(int x, int y) :
__x(x), __y(y),
__x_cnt(0), __a(4) {
Point::point_cnt += 1;
}
Point::~Point() {
Point::point_cnt -= 1;
}
int Point::x() const {
this->__x_cnt += 1;
return this->__x;
}
int Point::y() const {
return this->__y;
}
int Point::x_cnt() const {
return this->__x_cnt;
}
void Point::set_x(int x) {
this->__x = x;
}
void Point::set_y(int y) {
this->__y = y;
}
void func() {
Point c, d;
cout << "func : " << Point::output_cnt() << endl;
return ;
}
int main() {
Point a(2, 3), b;
const Point c(3, 4);
cout << a.x() << " " << a.y() << endl;
cout << c.x() << " " << c.y() << endl;
cout << c.x() << " " << c.y() << endl;
cout << a.x_cnt() << " " << b.x_cnt() << " " << c.x_cnt() << endl;
cout << "befor func : " << Point::output_cnt() << endl;
func();
cout << "after func : " << Point::output_cnt() << endl;
return 0;
}
constexptr(c11)
编译期常量
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
//可以返回编译期常量
constexpr int func(int i) {
return 2 * i;
}
struct A {
//可以当成编译期常量去用
constexpr A(int x, int y):x(x), y(y){}
int x, y;
};
int main() {
int n;
const int c = 3;
cin >> n;
const int a = n + 1;//运行期常量,运行才能确定n的值
constexpr int d = c + 123;
constexpr int b = func(123) + 4545;//编译期就可知道的常量
func(n);
constexpr A e(2, 3);
return 0;
}
default
default(默认、缺省)用于 switch 语句。当 switch 所有的 case 都不满足时,将进入 default 执行。default 只能放在 switch 语句所有的 case 之后,并且是可选的。
decltype(c11)
有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(初始化可以用auto)。为了满足这一需求,C++11新标准引入了decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
dynamic_cast
dynamic_cast (expr): dynamic_cast 在运行时执行转换,验证转换的有效性。如果转换未执行,则转换失败,表达式 expr 被判定为 null。dynamic_cast 执行动态转换时,type 必须是类的指针、类的引用或者 void*,如果 type 是类指针类型,那么 expr 也必须是一个指针,如果 type 是一个引用,那个 expr 也必须是一个引用。
class A{};
class B : public A{};
A *p;
dynamic_cast<B *> ( p );
p的类型为B则转换成功, 返回地址, 失败则返回空地址
必须有虚函数表
explicit
explicit(显式的)的作用是"禁止单参数构造函数"被用于自动型别转换,其中比较典型的例子就是容器类型。在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数。
指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。
struct A
{
A(int) { } // 转换构造函数
A(int, int) { } // 转换构造函数 (C++11)
operator bool() const { return true; }
};
struct B
{
explicit B(int) { }
explicit B(int, int) { }
explicit operator bool() const { return true; }
};
int main()
{
A a1 = 1; // OK:复制初始化选择 A::A(int)
A a2(2); // OK:直接初始化选择 A::A(int)
A a3 {4, 5}; // OK:直接列表初始化选择 A::A(int, int)
A a4 = {4, 5}; // OK:复制列表初始化选择 A::A(int, int)
A a5 = (A)1; // OK:显式转型进行 static_cast
if (a1) ; // OK:A::operator bool()
bool na1 = a1; // OK:复制初始化选择 A::operator bool()
bool na2 = static_cast<bool>(a1); // OK:static_cast 进行直接初始化
// B b1 = 1; // 错误:复制初始化不考虑 B::B(int)
B b2(2); // OK:直接初始化选择 B::B(int)
B b3 {4, 5}; // OK:直接列表初始化选择 B::B(int, int)
// B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)
B b5 = (B)1; // OK:显式转型进行 static_cast
if (b2) ; // OK:B::operator bool()
// bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool()
bool nb2 = static_cast<bool>(b2); // OK:static_cast 进行直接初始化
friend
friend(友元)声明友元关系。友元可以访问与其有 friend 关系的类中的 private/protected 成员,通过友元直接访问类中的 private/protected 成员的主要目的是提高效率。友元包括友元函数和友元类。
#include<iostream>
#include<vector>
#include<cstdio>
#include<string>
using namespace std;
class MyHoom;
class GoodFriend{
public:
GoodFriend();
void visit();
MyHoom *m_MyHoom;
};
class MyHoom{
// friend class GoodFriend;
friend void GoodFriend::visit();
friend int getage();
public:
MyHoom();
string m_SittingRoom;
private:
string m_BedRoom;
int age;
};
GoodFriend::GoodFriend() {
this->m_MyHoom = new MyHoom;
}
void GoodFriend::visit() {
cout << "好朋友正在参观" << this->m_MyHoom->m_SittingRoom << endl;
cout << "好朋友正在参观" << this->m_MyHoom->m_BedRoom << endl;
}
MyHoom::MyHoom() {
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
int getage() {
MyHoom Tom;
cout << "age is " << Tom.age << endl;
}
int main() {
GoodFriend f;
f.visit();
return 0;
}
forword和move(c11)
move:强制变右值
forword;可变左值可变右值
移动构造: 临时变量的拷贝, 拷贝后临时变量释放, 用移动构造, 使效率提高
final
c++新标准中,定义了final关键字,该关键字的作用是防止子类重新定义基类的成员函数,该关键字应该是模仿java中的final. 1. 作用指定无法继承的类 指定无法在派生类中重写的虚函数2. 代码演示//指定无法继承的类class BaseClass final. … 另外,我们也可以在成员函数的后面加final.
如果想让子类继承的某函数不变, 都保持一致, 则用final.
new 和 delete
new(新建)用于新建一个对象。new 运算符总是返回一个指针。由 new 创建
delete(删除)释放程序动态申请的内存空间。delete 后面通常是一个指针或者数组 [],并且只能 delete 通过 new 关键字申请的指针,否则会发生段错误。
#include<iostream>
#include<vector>
#include<cstdio>
#include<string>
using namespace std;
class Person {
public:
Person() {
cout << "构造函数" << endl;
}
Person(int n) {
cout << "有参构造" << endl;
}
~Person(){}
private:
};
void test1() {
///堆区开辟数组, 一定会调用默认构造函数.
Person *p1 = new Person[10];
//释放数组时,需要加[]
delete[] p1;
}
void test2 () {
//栈区开辟数组, 可以没有默认构造.
Person p2[10] = {Person(10), Person(20)};
}
int main() {
test1();
cout << endl;
test2();
return 0;
}
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
有参构造
有参构造
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
构造函数
nullptr(c11)
nullptr:空地址, 可以转成任意其他类型的指针
null既可以代表空地址, 也可以代表常亮0
override和final(c11)
override: 在成员函数声明或定义中,override 说明符确保该函数为虚函数并覆盖某个基类中的虚函数。若此非真则程序为谬构(生成编译错误)。
struct A
{
virtual void foo();
void bar();
};
struct B : A
{
void foo() const override; // 错误:B::foo 不覆盖 A::foo
// (签名不匹配)
void foo() override; // OK:B::foo 覆盖 A::foo
void bar() override; // 错误:A::bar 非虚
};
final:指定某个虚函数不能在子类中被覆盖,或者某个类不能被子类继承。
当在虚函数声明或定义中使用时,final 说明符确保函数为虚并指定其不可被派生类覆盖。若这么做则程序为谬构(生成编译时错误)。
struct Base
{
virtual void foo();
};
struct A : Base
{
void foo() final; // Base::foo 被覆盖而 A::foo 是最终覆盖函数
void bar() final; // 错误: bar 不能为 final 因为它非虚
};
struct B final : A // struct B 为 final
{
void foo() override; // 错误:foo 不能被覆盖,因为它在 A 中是 final
};
struct C : B // 错误:B 为 final
{
};
static
static(静态的)静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为 0,使用时可改变其值。静态变量或静态函数,只有本文件内的代码才可访问它,它的名字(变量名或函数名)在其它文件中不可见。因此也称为"文件作用域"。在 C++ 类的成员变量被声明为 static(称为静态成员变量),意味着它被该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。类的静态成员变量必须在声明它的文件范围内进行初始化才能使用,private 类型的也不例外。
#include<iostream>
#include<vector>
#include<cstdio>
#include<string>
using namespace std;
class Person{
public:
int arr;
//静态成员变量, 所有成员对象共享同一份数据
static int num;
//静态成员函数, 所有成员对象共享同一个函数
static void func() {
num = 20;//静态成员函数只能访问静态成员函数,不能访问非静态成员函数.因为不知道要修改的是哪个对象的变量.
// arr = 30;
cout << "func" << endl;
}
};
int Person::num = 10;//静态成员变量需在类外初始化
void test1() {
Person p1;
p1.num = 20;
cout << "p1.num : " << p1.num << endl;
Person p2;
p2.num = 30;
cout << "p2.num : " << p2.num << " "
<< "p1.num : " << p1.num << " "
<< "Person.num: " << Person::num << endl;
}
int main() {
test1();
return 0;
}
p1.num : 20
p2.num : 30 p1.num : 30 Person.num: 30