c++ 些许知识点

const的使用

顶层const,底层const:

指针既可以是顶层const(top-level const), 也可以是底层const(low-level const)。顶层const可以表示任意的对象是常量。

const形参和实参:

当用实参初始化形参时,形参顶层const会被自动忽略掉。当形参有顶层const时, 传递给形参常量或非常量都是可以的。

void fun(const int i);
void fun(int i);

这两者重复定义了fun(int i)。

尽量使用常量引用:

当函数不需要对对象的值进行修改时,尽量用常量引用。这样可以把const对象, 字面值或者需要类型转换的对象传给普通的引用形参。使用普通引用会减少函数可以接受的实参类型。

表达式

sizeof

对于sizeof p, 其等效于sizeof (p). 但是sizeof不会实际求运算对象的值,即使p是一个空指针,在sizeof里对其解引用也是安全的行为。sizeof不需要解引用对象也能知道对象的类型并返回对象类型所占空间的大小。

int arr[10];
int *p = arr;
cout<<sizeof(arr)/sizeof(*arr);
//输出10

强制转换

const_cast

只能改变运算对象的底层const且只能改变常量的类型。

reinterpret_cast

为运算对象提供较低层次的重新解释。

函数

返回数组指针

使用类型别名

typedef int arrt[10];     //arrt 是一个类型别名,表示含有十个整数的数组
using arrt = int[10];      //arrt 的等价声明
arrt* func(int i);            //声明返回指向有十个整数的数组指针的函数

不采用类型别名
int (*func(int i))[10];
尾置返回类型
auto func(int i) -> int(*)[10];
使用decltype;
int odd[5] = {1,2,3,4,5}; decltype(odd) *func(int i); //注意,decltype函数只会返回数组的类型而不是对应的指针,若果要让函数返回数组指针需要在函数名前加上*

返回引用的函数。

这类函数的返回值可以作为左值。

内联函数

将函数指定为内联函数,函数就会在调用处“内联地”展开成对应的语句(内联函数只是向编译器发出请求,编译器可以忽略)

constexpr函数

指能用于常量表达式的函数。定义该类型函数时需要遵守以下几条约定;函数的返回和形参类型都应该是字面值类型,且函数体中只有一条return语句。
注:内联函数和constexpr函数应该定义在头文件中。

NDEBUG预处理变量

可以把调试代码写在#ifndef NDEBUG中,在编译时可以通过命令行选项选择定义预处理变量来开关调试。
$ CC -D NDEBUG main.C

返回函数指针

类似返回数组指针

 auto fun(int, int) -> int(*)(int, int);    //尾置类型

常量成员函数

通过在成员函数参数列表后加上关键字const来让this变成指向常量的指针来提高函数的灵活性。

类型成员

不同于普通成员变量, 类型成员一定要先定义后使用。
如果在类内部使用了外部定义的类型名,之后就不能再重复定义该类型名。

可变数据成员

在声明数据成员前加上关键字mutable, const成员函数也可以改变可变成员的值。

构造函数的初始值

对于引用、常量之类的数据成员,初始值必不可少。

委托构造函数

class A{
public:
    A(int i, const std::string j):val(i), s(j){}  //非委托构造函数接收对应的实参进行初始化
	A(): A(0,""){} //定义的默认构造参数将工作委托给两参数构造参数
	A(const std:: string s): A(0, s){}  //同样将任务委托给了两参数构造函数
	A(std::istream &is): A(){is>>val; is>>s;} //先将任务委托给默认构造函数,在执行完委托的构
	                                          //造函数后最后再执行该函数体里的语句
private:
	int val;
	std::string s;
};

隐式的类类型转换

class A{
public:
	A &combine(A b);//省去其他定义(同上)
};
A:: A &combine(A b){
	this->i += b.i;
	return *this;
}
A item;
std:: string s = "123456";
item.combine(s); //这里combine成员函数需要一个A类类型的实参,编译器会自动的调用A的构造函
                    //数生成一个A类对象,同样也可以用istream类实参替代。
item.combine("123456"); //而这样会报错,因为编译器只支持一步类型转换。
item.combine(string("123456")); //可以通过显式的转换。

抑制构造函数定义的隐式转换

在构造函数前声明explicit可以阻止构建函数隐式地创建该类对象

...
explicit A(const std:: string s){}
...
item.combine(s) //错误:string构造函数是explicit的

explicit只对只有一个实参的构造函数有用。需要多个实参的构造函数无法进行隐式转换,所以explicit对其无效。只能在类内声明构造函数时使用expilcit,在外部定义时不应重复。
另外explicit构造函数只能用于直接初始化而不能舒勇拷贝形式的初始化
A item(s); //正确 A item() = s;//错误:不能用于拷贝形式的初始化。

聚合类

struct Data{
	int val;
	string s;
}

聚合类需满足以下条件:

  • 所有成员都是public
  • 没有定义任何构造函数
  • 没有类初始值
  • 没有基类,也没有virtual函数
    很明显Data是一个聚合类,可以通过以下方式初始化数据成员
    Data val_1 = {0, "Anna"};

字面值常量类

字面值常量是聚合类或者是满足以下条件的类。

  • 数据成员必须都是字面值类型。
  • 类必须至少含有一个constexpr构造函数。
  • 如果类的数据成员含有类内初始值,则其类内初始值必须是一个常量表达式;或者如果成员属于某种类型, 则 其初始值必须使用其constexpr构造函数。
  • 类必须使用析构函数的默认定义,该函数负责销毁类的对象。

类的静态成员

通常类的静态成员不应该在类的内部初始化。但我们可以为静态成员提供const整数类型初始值。
静态成员可以定义不完全类型

class Bar{
public:
	//...
private:
	static Bar mem1;  //正确
	Bar *mem2;        //正确
	Bar mem3;	  //错误: 数据成员必须是完全类别
}

未完待续...

posted @ 2020-10-16 23:03  kingchou  阅读(79)  评论(0)    收藏  举报