CPP基础
0. 如果没有指明访问限定符(public,private),class中默认的private,而struct中的成员默认是public的。
#include <iostream> using namespace std; int main() { cout<<"Hello world"<<endl; return 0; }
变量类型名:typeid(T).name()。
1. 存储
类的对象中只保存非静态成员变量,不保存成员函数,sizeof求得的大小只是非静态成员变量占用的空间。
只有非静态成员变量才属于类的对象上。
成员函数不占用对象空间,所有函数共享一个函数实例。
C++中定义的struct无成员时,也占用一个字节;而在C语言中不占用空间。
2.命名空间namespace
using namespace std;声明后,该命名空间的函数可直接调用。
函数前可不加命名空间,std::cout, std::endl。
此外,程序有一个默认的无名命名空间,若调用全局变量,可::cout(默认的无命名空间函数或变量)。
3.C++中定义了布尔类型
bool类型变量可赋值为true或false。
4.引用类型
数据类型 &别名 = 原名;
引用相当于为某个变量起一个别名,不占用存储空间,注意引用操作的是变量,不是常量。
#include <iostream> using namespace std; int main() { int m = 10; int& n = m; cout << "addr of m is " << &m <<endl; cout << "addr of n is " << &n <<endl; return 0; } // addr of m is 0x7fff53d3e1ac // addr of n is 0x7fff53d3e1ac
规则:
1)引用被创建的同时必须初始化(指针则可以在任何时候初始化)。
2)不能有NULL引用,引用必须与合法的存储单元关联。
3)一旦引用被初始化,就不能改变引用的对象。
int &ref = 10; 错误,引用本身需要一个合法的内存空间。
const int &ref = 10;正确,编译器优化代码,int tmp = 10; const int &ref = tmp;
应用
引用的主要功能是传递函数的参数和返回值。
void swap(int &x, int &y) { int temp; temp = x; x = y; y = temp; }
传引用,实为传变量本身。
char arr[10] = {'a', 'b', 'c'}; char & foo(int i) { return arr[i]; } foo(1) = 'h';
引用可以作为函数的返回值存在,注意函数不能返回局部变量引用。
引用作函数的返回值,可以作左值。若不想其做左值,可加const修饰。
常量引用
主要用来修饰形参,防止误操作。在函数形参列表中,可以加const修饰形参,防止形参改变实参。
引用的本质
应用的本质在C++内部实际是一个指针常量,但是所有的指针操作编译器都帮我们做了。
#include <iostream> #include <cstdlib> #include <unistd.h> #include <string> using namespace std; // 编译器发现是引用,转换为 int *const ref = &a void func(int &ref) { ref = 100; // ref是引用,转换为*ref = 100 } int main() { int a = 10; //自动转换为int *const ref = &a; 指针常量指向不变,指向的值可以改变 int &ref = a; ref = 20; // 内部发现ref是引用,自动帮我们转换为:*ref = 20 cout << "a = \t" << a << endl; cout << "ref = \t" << ref << endl; func(a); cout << "a = \t" << a << endl; cout << "ref = \t" << ref << endl; return 0; } ----------------------------- a = 20 ref = 20 a = 100 ref = 100
5. new和delete为C++关键字
// 简单类型 int * p = new int(10); delete p; //数组 int *arr = new int[10]; delete [] arr;
new和malloc在heap上分配的空间地址不同,因此malloc和free配对使用,new和delete配对使用。
6. 函数
函数的缺省参数
c++在声明函数时允许给某些参数指定缺省值。
参数缺省值一般写在声明中。缺省参数从右向左连续赋值。
函数占位参数
C++的形参列表里可以有占位参数,用来占位、调整函数时必须填补该位置。
void funcb(int a, int = 10) { cout << "This is func. " << endl; } func(10, 100); func(10);
7.inline函数
c++中用inline函数来取代C语言中类似函数的宏定义。
使用内联函数时注意inline关键字应该和定义放在一起,而不是和声明放在一起。
由于inline函数相当于宏定义,因此通常将inline函数的实现直接放在头文件中,不写声明。
内联函数优化级为O2。
8. 在C++代码中调用C的库函数, 让C函数可以运行在C++环境中(C++支持函数重载,编译时符号名为函数名+参数类型)。
#ifdef __cplusplus extern "C" { #endif int add(int a, int b); #ifdef __cplusplus } #endif
9. C++支持函数overload,同一作用域下,两个或两个以上的函数,取相同的函数名,但是形参的个数或类型或顺序不同,编译器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。
int add(int, int)在底层被重载为addii,double add(double, double)在底层被重载为adddd。
用nm命令察看可执行文件的符号表。
注:函数的返回值不可以作为函数重载的条件。
注:当函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况。
void func2(int a) 和 void func2(int a, int b = 10)
注:引用作为重载条件时注意
#include <iostream> #include <cstdlib> #include <unistd.h> #include <string> using namespace std; void func(int &a) // int &a = 10; 不合法 { cout << "func(int &a)" << endl; } void func(const int &a) // const int &a = 10;合法 { cout << "func(const int &a)" << endl; } int main() { int a = 10; func(a); // 调用 func(int &a) const int b = 20; func(b); // func(const int &a) func(100); // func(const int &a) // invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’ // int &c = 10; const int &d = 100; return 0; }
10. const
const修饰指针,有三种情况:
1)const修饰指针--常量指针;
const int *p1 = &a;
指针指向可以改(p1 = &b),指针指向的值不可以修改(*p1 = 100,错误)。
2)const修饰常量--指针常量;
int *const p2 = &a;
指针指向不可以改(p2 = &b 错误),指针执行的值可修改(*p2 = 100)。
3)const既修饰指针又修饰常量;
const int *const p3 = &a;
指针的指向和指针指向的值都不可以更改。
技巧:const右侧紧跟的是指针就是常量指针,是常量(或变量)就是指针常量。
11. NULL和nullptr区别
在C语言中,NULL通常被定义为:#define NULL ((void *)0)
C++是强类型语言,不能把void*类型的指针隐式转换成其他类型的指针,为了解决空指针的表示问题,C++引入了nullptr来表示空指针。
#ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif
nullptr是一个字面值常量,类型为std::nullptr_t,空指针常数可以转换为任意类型的指针类型。
void fun(int i){cout<<"1";}; void fun(char *p){cout<<"2";}; int main() { fun(NULL); //输出1,c++中NULL为整数0 fun(nullptr);//输出2,nullptr 为空指针常量。是指针类型 }
NULL和nullptr都是代表空指针,但是NULL在重载函数的时候却匹配到了参数为int的那个版本。
NULL在C++中就是0,这是因为在C++中void* 类型是不允许隐式转换成其他类型的,所以之前C++中用0来代表空指针,但是在重载整形的情况下,会出现上述的问题。所以,C++11加入了nullptr,可以保证在任何情况下都代表空指针,而不会出现上述的情况,因此,建议以后还是都用nullptr替代NULL吧,而NULL就当做0使用。
12. static_cast、dynamic_cast、const_cast和reinterpret_cast
参考:C++中的类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast总结
static_cast
static_cast的转换格式:static_cast <type-id> (expression)
将expression转换为type-id类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。
dynamic_cast
dynamic_cast的转换格式:dynamic_cast <type-id> (expression)
将expression转换为type-id类型,type-id必须是类的指针、类的引用或者是void *;如果type-id是指针类型,那么expression也必须是一个指针;如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast,因为类型提供了运行时信息。
const_cast
const_cast的转换格式:const_cast <type-id> (expression)
const_cast用来将类型的const、volatile和__unaligned属性移除。常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。
注:你不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。
reinterpret_cast
reinterpret_cast的转换格式:reinterpret_cast <type-id> (expression)
允许将任何指针类型转换为其它的指针类型;听起来很强大,但是也很不靠谱。它主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针,在实际开发中,先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原来的指针值;特别是开辟了系统全局的内存空间,需要在多个应用程序之间使用时,需要彼此共享,传递这个内存空间的指针时,就可以将指针转换成整数值,得到以后,再将整数值转换成指针,进行对应的操作。
13. 原子操作
参考:C++
中的原子操作和原子类型 C++并发编程中文版 C++11 - atomic类型和内存模型
标准 原子类型 定义在头文件<atomic>
中,其中定义的原子类型如atomic_bool,atomic_int等可以执行原子的++,--或复制或读取操作。
而与之对应的模板类std::atomic<bool>,std::atomic<int>提供原子操作成员函数如load()和store()等。
std::atomic<int64_t> value; value++; // 此处的原子,指的是读取value的值,这一步。 // 而不是,将value的值赋给x。 int64_t x = value.load(std::memory_order_relaxed); int64_t x = 10; value.store(x,std::memory_order_relaxed)
通常情况下,内存模型是一个硬件上的概念,表示的是机器指令(或者将其视为汇编指令也可以)是以什么样的顺序被处理器执行的。现代的处理器并不是逐条处理机器指令的。在C++11标准中,设计者给出的解决方式是让程序员为原子操作指定所谓的内存顺序:memory_order。
memory_order参数的默认值是std::memory_order_seq_cst。实际上,atomic类型的其他原子操作接口都有memory_order这个参数,而且默认值都是std::memory_order_seq_cst。
拓展:
1. 设计模式 刘利朋 海洋大学 https://llpspark.github.io/index.html
2. 面向对象程序设计与原则
4. 黑马程序员 C++ https://www.bilibili.com/video/BV1et411b73Z?p=169&spm_id_from=pageDriver
C++| 匠心之作 从0到1入门学编程 简书 讲义
https://github.com/yf-leung/c-bzhan github
5. C++并发编程(中文版)