常量
- 整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
- 浮点常量由整数部分、小数点、小数部分和指数部分组成,如下所示。
3.14159 // 合法的 314159E-5L // 合法的 510E // 非法的:不完整的指数 210f // 非法的:没有小数或指数 .e55 // 非法的:缺少整数或分数
- 布尔常量共有两个,我们不应把 true 的值看成 1,把 false 的值看成 0。
- 请注意,把常量定义为大写字母形式,是一个很好的编程实践。
变量作用域
- 局部作用域、全局作用域、块作用域、类作用域
- 局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值,如下所示输出为10。
#include <iostream> using namespace std; // 全局变量声明 int g = 20; int main () { // 局部变量声明 int g = 10; cout << g; return 0; }
数据类型
- 常见的数据类型菜鸟-C++ 数据类型
- 常用的关键字参考菜鸟-C++ 的关键字(保留字)完整介绍
类型转换
- 类型转换是将一个数据类型的值转换为另一种数据类型的值。C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
- 静态转换是将一种数据类型的值强制转换为另一种数据类型的值。静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型。
int i = 10; float f = static_cast<float>(i); // 静态将int类型转换为float类型
- 动态转换(dynamic_cast)是 C++ 中用于在继承层次结构中进行向下转换(downcasting)的一种机制。动态转换通常用于将一个基类指针或引用转换为派生类指针或引用。dynamic_cast<目标类型>(表达式)
指针类型动态转换: #include <iostream> class Base { public: virtual ~Base() = default; // 基类必须具有虚函数 }; class Derived : public Base { public: void show() { std::cout << "Derived class method" << std::endl; } }; int main() { Base* ptr_base = new Derived; // 基类指针指向派生类对象 // 将基类指针转换为派生类指针 Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); if (ptr_derived) { ptr_derived->show(); // 成功转换,调用派生类方法 } else { std::cout << "Dynamic cast failed!" << std::endl; } delete ptr_base; return 0; }
引用类型动态转换 #include <iostream> #include <typeinfo> class Base { public: virtual ~Base() = default; // 基类必须具有虚函数 }; class Derived : public Base { public: void show() { std::cout << "Derived class method" << std::endl; } }; int main() { Derived derived_obj; Base& ref_base = derived_obj; // 基类引用绑定到派生类对象 try { // 将基类引用转换为派生类引用 Derived& ref_derived = dynamic_cast<Derived&>(ref_base); ref_derived.show(); // 成功转换,调用派生类方法 } catch (const std::bad_cast& e) { std::cout << "Dynamic cast failed: " << e.what() << std::endl; } return 0; }
- 常量转换用于将 const 类型的对象转换为非 const 类型的对象。常量转换只能用于转换掉 const 属性,不能改变对象的类型。
const int i = 10; int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
- 重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
int i = 10; float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型
修饰符类型
- C++ 允许在 char、int 和 double 数据类型前放置修饰符,其他类型不能放修饰符。
- signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。
- 类型限定符如下 :register用于建议编译器将变量存储在CPU寄存器中以提高访问速度。在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。在 C++17及以后的版本中,auto 已经是一个废弃的特性,不再具有实际作用。
存储类
- 存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:
循环
- 基于范围的for循环
int my_array[5] = {1, 2, 3, 4, 5}; // 每个数组元素乘于 2 for (int &x : my_array) { x *= 2; cout << x << endl; } // auto 类型也是 C++11 新标准中的,用来自动获取变量的类型 for (auto &x : my_array) { x *= 2; cout << x << endl; } //示例3 int main() { string str("some string"); // range for 语句 for(auto &c : str) { c = toupper(c); } cout << str << endl; return 0; }
函数参数
- 传值调用就是最普通的调用形式,不做讲解。
- 指针调用,向函数传递参数的指针调用方法,把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
// 函数定义 void swap(int *x, int *y) { int temp; temp = *x; /* 保存地址 x 的值 */ *x = *y; /* 把 y 赋值给 x */ *y = temp; /* 把 x 赋值给 y */ return; }
- 向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
1、引用定义:通过使用&符号来定义引用类型,例如int &ref。
2、传递引用:在函数参数列表中使用引用类型,这样函数内部的修改会影响到实际传递的变量。
3、避免拷贝开销:引用传递避免了传递大对象时的拷贝开销,提高了效率。// 函数定义 void swap(int &x, int &y) { int temp; temp = x; /* 保存地址 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return; }
Lambda函数
暂未深入了解
数组
- C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。
- C++ 不允许返回一个完整的数组作为函数的参数。但是可以通过指定不带索引的数组名来返回一个指向数组的指针。不能简单地返回指向局部数组的指针,因为当函数结束时,局部数组将被销毁,指向它的指针将变得无效,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。为了避免以上情况,可以使用静态数组或者动态分配数组。
指针
- NULL指针:在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
- 指针的算术运算:指针算术运算会根据指针的类型和大小来决定移动的距离。由于是一个 32 位整数指针,每个整数占据 4 个字节,因此 ptr++ 会将指针 ptr 向前移动 4 个字节,指向下一个整型元素的地址。
- 指针的比较操作可以用于确定两个指针是否指向相同的位置、一个指针是否指向的位置在另一个指针之前或之后等。指针的比较主要包括以下几种:相等性比较 (== 和 !=),关系比较 (<, <=, >, >=)。
- 指针数组,如下所示,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。
上述两种表达引发了一个问题:为啥char* 是字符串,int* 是指针,int *需要解引用,而char *不需要?int *ptr[MAX];// cout << *ptr[i] << endl; const char *names[MAX] = { "Zara Ali", "Hina Ali", "Nuha Ali", "Sara Ali", }; cout << names[i] << endl;
原因:C++ 的 iostream(特别是 cout)对 char* 类型有专门的输出规则,而 int* 和其他指针类型没有这种特殊待遇。int* 只是一个普通指针,cout 不知道它指向的是单个 int 还是数组。char* 在 C/C++ 中默认被视为 C 风格字符串的起始地址。 - 指向指针的指针:一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。
- 传递指针给函数:能接受指针作为参数,也能接受数组作为参数。
#include <iostream> #include <ctime> using namespace std; // 在写函数时应习惯性的先声明函数,然后在定义函数 void getSeconds(unsigned long *par); int main () { unsigned long sec; getSeconds( &sec ); // 输出实际值 cout << "Number of seconds :" << sec << endl; return 0; } void getSeconds(unsigned long *par) { // 获取当前的秒数 *par = time( NULL ); return; }
- 从函数返回指针
int * getRandom( ) { static int r[10]; // 设置种子 srand( (unsigned)time( NULL ) ); for (int i = 0; i < 10; ++i) { r[i] = rand(); cout << r[i] << endl; } return r; }
引用
- 引用与指针的区别: 1、不存在空引用,引用必须连接到一块合法的内存。2、一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。3、引用必须在创建时被初始化。指针可以在任何时间被初始化。4、引用的对象必须是一个变量,而指针必须是一个地址。
- 引用作为参数:
// 函数定义 void swap(int& x, int& y) { int temp; temp = x; /* 保存地址 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return; }
- 引用作为返回值:当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。以下是两种引用方式,第一种不需要static进行修饰,是因为ref 是一个引用变量,它直接绑定到全局数组 vals[i],并不是局部变量。
double& setValues(int i) { double& ref = vals[i]; return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i] } int& func() { int q; //! return q; // 在编译时发生错误 static int x; return x; // 安全,x 在函数作用域外依然是有效的 }