C++ 基础语法 一
C++ 基础语法 一
文章目录
- C++ 基础语法 一
- const 限定符
- 常量指针
- 类型别名
- auto
- decltype
- QString
- vector
- 迭代器
- 指针和数组
- 显示转换
- static_cast
- const_cast
- 函数
- 尽量使用常量引用
- 数组形参
- 不要返回局部对象的引用和指针
- 返回数组指针
- C++四种转换
- 内联函数
- constexpr函数
- 函数指针
const 限定符
const对象一旦创建后 他的值就不能再改变 所以const对象必须初始化,常量特征只有是在执行改变其值才会发挥作用,默认情况下,const对象仅在文件内有效,多个文件内有同名const 变量其实是等同于不同文件分别定义了独立变量。如果要多个文件共享一个const变量 需要使用 extern
exten const int bufSize = 512 该常量可以被其他文件访问
常量指针
int i=42;
int *const curErr = &i; // 把 * 放在const 前面代表这个指针是一个常量,不能改变指正本身的值 而非指向的那个值
cout<<curErr<<" "<<*curErr<<endl;
// 0x61fe14 42
i=5;
cout<<curErr<<" "<<*curErr<<endl;
// 0x61fe14 5
const double pi = 3.1415926;
const double *const pip = π // pip是一个常量对象的常量指针
类型别名
typedef double wages; //wages 是double的别名
using si = sales_item; // si是sales_item的别名
auto
// 使用auto 也能在一条语句中声明多个变量,因为声明语句只有一个基本数据类型,所以该语句中所有的变量的初始基本数据类型必须都一样
auto i = 0,*p = &i; // 正确
auto sz = 0,pi = 3.14; // 错误 sz和pi类型不一致
decltype
选择并返回操作数的数据类型
decltype(f()) sum = x // sum类型就是函数f的返回类型
QString
string s1 = "hello ";
string s2 = "world";
string s3 = s1+s2;
cout<<&s1<<" "<<&s2<<" "<<&s3<<endl;
s1 += s2; // 直接在s1后面追加 不会创建对象
cout<<&s1<<" "<<&s2<<endl;
当把string对象和字符字面值以及字符串字面值混在一条语句使用时,必须确保每个加法运算符的
两侧只有有一个是string,字符串的字面值不是string 是 const char []
string s1("hello world!!!");
int punct_cnt = 0;
for(auto c : s1){
if(ispunct(c)){
punct_cnt++;
}
} 统计是标点符号的个数
// 通过引用修改范围变量的本身的值
string s1("hello world!!!");
for(auto &c : s1){
c = toupper(c);
}
cout<<s1<<endl;
vector
本身是模板而非类型,由vector生成的类型必须包含vector中元素的类型,例如vector,模板本身不是类或函数 ,相反可以将模板看做为编译器生成类或函数编写的一份说明。编译器根据模板创建的类或函数的过程称为实例化。
// 如果初始化时使用了花括号的形式但是提供的值又不能用来初始化,这时候就要考虑用这样的值
来构造vector对象了。
vector<int> v1{10}; //初始化v1 有一个元素 就是10
printf("%d\n",v1.size());
vector<string> v2{10}; // 10个空字符串
printf("%d\n",v2.size());
迭代器
标准库类型使用iterator和const_iterator来表示迭代器类型
vector<int>::iterator it; // it 可以读写vector<int>的元素
string::iterator it2; // it2 可以读写string的元素
vector<int>::const_iterator it3; // it3 只能读元素 不能写元素
string::const_iterator it4; // it4 只能读元素 不能写元素
const_iterator 和常量指针差不多 能读取但不能修改它所指的元素值 ,相反iterator可读可写。为了方便我们得到const_iterator 类型返回值 ,C++11提供了两个新函数 分别是cbegin 和 cend;
指针和数组
在C++语言中指针和数组有着非常密切的联系 ,使用数组的时候编译器会把他转换为指针,在很多用到数组名字的地方,编译器都会自动的将其转换为一个指向数组首元素的指针。指向数组的指针拥有更多功能,数组的指针全部支持,特别注意尾后指针不能执行解引用和递增操作
int arr[] = {1,2,3,4,5};
for(int * b = arr;b!=arr+5;b++){
cout<<*b<<" ";
}
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int *beg = begin(arr); //beg 指向arr的第一个元素 begin 是定义在iterator头文件中的一个函数 ,用来返回一个指向arr首元素的指针
int *last = end(arr); //last 指向arr的尾元素的下一个位置 end 是定义在iterator头文件中的一个函数 ,用来返回一个指向arr尾元素的下一个位置的指针
while (beg != last)
{
cout<<*beg<<endl;
++beg;
}
c标准库string函数 ,使用此类函数的指针必须指向以空字符作为结束的数组,例如:
char ca[] = {'C','+','+'}; //不是一个以空字符结束的字符串
cout<<strlen(ca)<<endl; //严重错误 strlen是一个计算以空字符结束的字符串长度的函数,但是ca不是一个以空字符结束的字符串,
strlen函数将有可能沿着ca之后的内存继续查找,直到找到一个空字符为止
不能直接用string对象直接初始化指向字符的指针。为了完成该功能,string专门提供了一个名为c_str 成员函数
char * string = s // 错误 不能用string对象初始化char*
const char * str = s.c_str() // 正确
可以使用数组初始化vector对象 ,允许使用数组来初始化vector对象 。要实现这一目的只需要指明拷贝区域的首元素地质和尾后地址就行。
int int_arr[] = {1,2,3,4,5,6,7,8,9,0};
vecotr<int> ivec(begin(int_arr),end(int_arr));]
最外层的循环控制变量声明成引用类型 ,这是为了避免数组被自动转换成指针 ,如果不用引用类型 编译器会初始化row 时会指向数组首元素的指针 这样便是得到int * ,显然内循环就不合法了,要使用范围for语句处理多维数组 除了最内层的循环外,其他的所有的循环变量都应该是引用类型
int ia[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
for(auto &row : ia){
for (auto col : row)
{
cout<<col<<endl;
}
}
显示转换
一个命名的强制转换具有如下形式: cast-name<type> (expression);
case-name 是 static_cast dynamic_cast const_cast reinterpret_case
static_cast
任何具有明确意义的类型转换,只要不包含底层的const,都可以使用static_cast。通过一个运算对象强制转换成double类型就能使表达式执行浮点数除法
// 进行强制类型转换
double slope = static_cast<double>(5)/2;
static_cast 对于编译器无自动执行的类型转换也非常有用。例如我们可以使用static_cast 找回存在的void* 指针 强制转换结果将与原始的地址值相等。
int d = 3.14;
void * p = &d; // void * 指针可以存放任意对象的地址
double *dp = static_cast<double*>(p); // 不能直接赋值,需要强制类型转换
const_cast
const_cast只能改变运算对象的底层const
const char *pc;
char *pcc = const_cast<char*>(pc); // const_cast 只能改变底层const 通过p写值是未定义的行为
对于将常量对象转换成非常量对象的行为,我们一般称其去const 性质,一旦去掉了某个对象的const性质,编译器就不在阻止我们对该对象进行写操作。只有const_cast 能改变表达式的常量属性。
函数
尽量使用常量引用
把函数不会改变的形参定义成普通的引用是一种比较常见的问题,这么做会给函数调用者带来一个误导即函数可以修改他的实参值。此外使用引用而非常量引用会极大的显示函数所能接受的实参类型。我们不能把const对象 ,字面值或者需要类型转化的对象传递给普通的引用形参。
数组形参
数组的两个特殊性质:不允许拷贝数组以及使用数组时会将其转换成指针,因为不能直接拷贝数组,所以不能使用值传递的方式使用数组参数,因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际传递的是指向数组首元素的指针。
当和其他使用数组的代码一样,以数组作为形参的函数必须确保使用数组不会越界。当函数不需要对数组元素执行写操作的时候,数组形参应该是指向const的指针,只有当函数确实要改变元素的时候,才把形参定义成指向非常量的指针
void print(int (&arr)[10]){ // c++ 允许将变量定义成数组的引用
for(auto i:arr){
cout<<i<<" ";
}
}
f(int &arr[10])
arr是引用数组
f(int (&arr)[10]
arr是具有10个整数的整形数组的引用
C++ 引用的数组和数组的引用-CSDN博客
不要返回局部对象的引用和指针
函数完成之后,他所占用的存储空间也会随之被释放,因此函数终止意味着局部变量的引用将指向不在有效的内存区域。main 函数不能调用自己
返回数组指针
数组不能拷贝,所以函数不能返回数组,不过,函数可以返回数组的指针或引用,想要定义一个返回数组的指针或引用的函数比较繁琐,但是有一些方法可以简化这一任务,其中最直接的方法就是使用类型别名
C++四种转换
内联函数
- 内联函数可以避免函数调用的开销,将函数指定为内联函数 ,通常就是将它在每一个调用点上内联的展开 例如
cout<<shorterString(s1,s2) <<endl;在编译过程中会自动展开成’cout << (s1.size()<s2.size() ? s1 : s2)<<endl;
- 内联说明只是向编译器发出的一个请求,编译器可以忽略这个请求。
- 内联机制用于优化规模较小,流程直接的频繁调用的函数,很多编译器都不支持内联的递归调用
constexpr函数
- constexper 函数是指用于常量表达式的函数,函数的返回类型及其所有的形参的类型都得是字面值类型。而且函数体中必须有且仅有一条return 语句
constexper int new_sz() {return 42;}
- 执行该语句时,编译器对把constexper函数的调用替换成其结果值,为了能在编译过程中随时展开,constexper函数被隐式的指定为内联函数,constexper函数体内也可包含其他语句,只要这些语句在运行时不进行任何操作就行。
constexper size_t scale(size_t cnt) return {new_sz() * cnt;} 如果arg是常量表达式,则scale(arg) 也是常量表达式
- 把内联函数和constexper函数 放在头文件内,内联函数和constexper函数可以在程序中多次定义,他的多个定义必须完全一致
函数指针
- 函数指针指向的是函数而非对象,和其他指针一样,函数指针指向某种特定类型,函数的类型由他返回类型和形参类型共同决定,与函数名无关。
比较两个string 对象长度bool lengthCompare(const string& ,const string &);该函数类型是bool (const string &,const string &)。 想要声明一个可以指向该函数的指针,只需要用指针替换函数名即可// pf 指向一个函数,该函数的参数是两个const string 的引用,返回值是bool类型bool (*pf) (const string &,const string &);pf 两端的括号必不可少,如果不写这对括号,则pf是一个返回值为bool指针的函数
- 当我们把函数名作为一个值使用时,该函数自动的转换成指针。
按照如下形式我们可以将lengthCompare的地址赋值给pf;pf = lengthCompare; pf指向名为lengthCompare的函数pf = &lengthCompare; 等价的赋值语句,取地址符是可选的
- 我们可以为函数指针赋值一个nullptr 或者 值为0的整形常量表达式,表示该指针没有指向任何函数。
- 重载函数的指针,当我们使用重载函数时,上下文必须清晰地界定到底应该选用那个函数,编译器会通过函数的形参列表和返回值确定选用那个函数,指针类型必须与重载函数中某一个精确匹配
- 函数指针形参和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针,此时,形参看起来是函数类型,实际上却是当成指针使用。
第三个形参是函数类型,他会自动转换成指向函数的指针
void userBigger(const string& s1,const string& s2 , bool pf(const string & , const string &));
等价声明:显示的将形参定义成指向函数的指针
void userBigger(const string& s1,const string& s2 , bool (*pf)(const string & , const string &));
我们可以直接把函数作为实参使用,此时他会自动转换成该函数的指针
userBigger(s1,s2,lengthCompare) 自动将函数lengthCompare 转换成指向该函数的指针
- 返回指向函数的指针,我们必须把返回的类型写成指针类型,编译器不会自动的将函数返回类型当成对应的指针类型处理,与往常一样要想声明一个返回函数指针的函数,最简单的办法是使用类型别名。
using F = int(int*,int);
F是函数类型,不是指针
using PF = int(*)(int*,int);
PF是函数指针 ---- 这里和函数类型的形参不一样,返回类型不会自动的转换成指针,我们必须显式的将返回类型指定为指针
PF f1(int) 正确 F* f1(int) 正确 显式指定返回值类型指向函数的指针
F f1(int) 错误 不能返回一个函数