局部静态对象
局部静态对象在程序的执行路径第一次经过对象定义语句时间初始化,调用结束后,这个对象仍然有效,程序结束时销毁。内置类型局部静态变量默认初始化值为0
函数形参
void print(const int*);
void print(const int[]);
void print(const int[10]);
三者相同,但值得注意的是。const int[10]--->这里只是期望数组含有多少个元素,实际不一定是这么多
void print(int (&arr)[10]);//数组引用形参
--->&arr两端的括号不能少
f(int &arr[10])//错误:将arr声明成了引用的数组
f(int (&arr)[10])//正确:arr是具有10个整数的整型数组的引用-----》数组大小需要相同,不能大,也不能小
main
- int main(int argc,char **argv){}
当使用argv中的实参时,一定要记得可选的实参从argv[1]开始,argv[0]是保存程序的名字,而非用户输入
可变参数
-
initializer_list
-
操作 解释 initializer_list lst; 默认初始化;T类型元素的空列表 initializer_list lst{a,b,c...} lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const lst2(lst) 拷贝或者赋值一个Initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 lst2=lst 同lst2(lst) lst.size() 列表中的元素数量 lst.begin() 返回指向lst中首元素的指针 lst.end() 返回指向lst中尾元素的下一个位置
-
-
使用
void error_msg(initializer_list<string> li)
{
for(auto beg=li.begin();beg!=li.end();++beg)
cout<<*beg<<" ";
cout<<endl;
}
- 省略符
大多数类类型的对象在传递给省略符形参时都无法正确拷贝
void foo(parm_list,...);//逗号可省略
void foo(...);
重载
- const---->无法重载顶层const
void f(Phone);
void f(const Phone);//重复声明,顶层const
void f(Phone*);
void f(Phone* const);//重复声明,顶层const
void f(Phone&);
void f(const Phone&);//正确:底层const
void f(Phone*);
void f(const Phone*);//正确:底层const
返回值
如果返回类型是常量--》引用,我们不能给调用的结果赋值。
shorterString("hi","bye")="X";//错误,返回值是个常量
默认实参
声明可以有很多次,但是带有默认实参的只能出现一次,其他均不带默认实参
inline和constexpr
内联说明只是想编译器发出一个请求,编译器可以选择忽略这个请求
类inline只需出现一次,类内或者内外定义时都可以
constexpr函数不一定返回常量表达式
内联函数和constexpr函数可以被多次---定义。但定义需一致
assert预处理宏
- 头文件:
#include <cassert>
- assert(expr);--->expr为假(0),assert输出信息并终止程序的执行,为真(非0),assert什么也不做
- 不是std::assert,是assert
函数指针
- 使用函数指针
bool lengthCompare(const string&,const string&);//函数
bool (*pf)(const string&,const string&);//未初始化的函数指针,pf指向一个函数,该函数返回类型bool, *pf两端括号不能省略
bool *pf(const string&,const string&);//函数,返回类型bool*
//使用
pf=lengthCompare;//pf指向名为lengthCompare的函数
pf=&lengthCompare;//等价赋值语句,&可省略
//亦或者
bool b1=pf("hello","goodbye");
bool b2=(*pf)("hello","goodbye");
bool b3=lengthCompare("hello","goodbye");//三者等价
- 函数指针重载
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int)=ff;//pf1指向ff(unsigned int)
void (*pf2)(int) =ff;//错误:没有任何一个ff匹配。指针类型必须精准匹配
double (*pf)(int*) = ff;//错误:ff和pf3返回类型不一致
- 返回指向函数的指针
using F = int(int*,int);//F是函数类型,不是指针
using PF=int(*)(int *,int);//PF是指针类型
PF f1(int);//正确
F f1(int);//错误:F是函数类型,f1不能返回一个函数
F *f1(int);//正确:显示的指定返回类型是指向函数的指针
int (*if(int))(int*,int);//直接声明函数指针
类
定义在类内部的函数是隐式Inline函数。
this
- 谁调用了该函数,this就指向谁
- 常量成员函数
- 参数列表后面跟着const的函数
- string isbn(const string) const;
- 常量对象,以及常量对象的引用或者指针都只能调用常量函数成员函数
- 所以这里的const是限制修改this的任何成员
- 常量成员函数
构造函数
无论何时只要类的对象被创建,就会执行构造函数
构造函数不能被声明为const,创建一个const对象时,直到构造函数完成初始化过程,对象才能真正取得“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。
只有当类没有声明---任何--构造函数时,编译器才会自动的生成默认构造函数
如果类包含有内置类型或者复合类型(比如数值指针)的成员,则只有当这些成员全都被赋予了类内的初始值时,这个类才适合使用于合成的默认构造函数
- 构造函数初始值列表的初始化顺序是有类内声明的对象顺序决定的,不是由列表内顺序决定的。不小心错误顺序极其容易引发未定义
- 如果成员是const,引用,或者属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初始值
class myClass
{
public:
myClass(int il);
private:
const int ci;
};
myClass::myClass(int il)
{
ci = il;//错误,无法给const赋值,必须通过初始值列表
}
myClass::myClass(int il) : ci(il){}//正确
//注意
myClass obj();//错误使用,声明一个函数而非对象
myClass obj;//正确
struct
和class和唯一区别就是默认访问权限
友元
友元不是类的成员,也不受它所在区域访问权限级别的约束。一般来说,声明于类定义的开始或者结束前的位置。如果需要调用某个友元函数,那么我们必须在友元声明之外再专门对函数进行一次声明。值得注意的是,大多数编译器都不会强制需求这样做。
友元关系不存在传递。无论是继承还是友元类都不会传递。
委托构造函数
- 一个委托构造函数把初始化过程转交给其他构造函数的过程
class myClass
{
public:
myClass(int i,char c,float f):i(i),c(c),f(f) {}
myClass():myClass(0,'a',3.14f){}//委托构造函数
myClass(int data):myClass(data,'a',3,14f){}//委托构造函数
myClass(char c):myClass() {cout<<c<<endl;}
private:
int i;
char c;
float f;
}
隐式的类类型转换
- 隐式转换(没有限制过的叫做转换构造函数)
string book= "C++";
void myclass::combine(myclass);
myClass.combine(book);//错误:传递一个myClass临时量,错误原因见explicit
//错误:需要用户定义的两种转换
//1)把"C++"转成string
//2)把临时的string转成myClass
myClass.combine("C++");
//正确,显示转成string,隐式转成myCalss
myClass.combie(string("C++"));
//正确,隐式转成string,显示转成myClass
myClass.combine(myClass("C++"));
myClass.combine(cin);--->错误:转递一个myclass临时量,错误原因见explicit
- 抑制隐式转换----explicit
explicit myClass(const string&);
explicit myClass(istream&);
--》关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的。只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复
myclass.combine(book);//错误,string构造函数时explicit的
myclass.combine(cin);//错误,istream的构造函数是explicit的
myClass obj(book);//正确,直接初始化
myClass obj=book;//错误:不能将explicit构造函数用于拷贝形式的初始化过程
字面值常量类(P267)
尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数。constexpr构造函数体一般来说是空的。---函数体空的,通过初始化列表初始化
类的静态成员
静态成员函数不与任何对象绑定在一起,他们不存在this指针。作为结果,静态成员函数不能声明成Const的,而却我们也不能在static函数体内使用this指针。这一限制即适用于this显示使用,也对调用非静态成员函数的隐式使用有效。
因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的.这意味着他们不是由类的构造函数初始化的。我们不能在类的内部初始化静态成员(一般来说)。相反的,必须在类的外部定义和初始化每个静态成员。和其他对象一样,一个静态数据成员只能定义一次。
类似于全局变量,静态数据成员定义在任何函数之外。一旦被定义,将一直存在整个程序生命周期。
一般来说,我们只定义一次静态数据成员。最好的办法时把它与它关联的函数定义在同一个头文件中。
即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类外定义一下该成员。
可以使用静态数据成员作为默认实参。非静态数据成员不能作为默认实参,因为它的值本身属于对象的一部分。(对象还没有就想生孩子了,引发错误)