CPP_HelloWorld
1 2 3 4 5 6 7 8 #include <print> int main (int argc, char * argv[]) { std::println ("hello world" ); return 0 ; }
1 2 3 4 5 6 7 8 #include <iostream> int main (int argc, char * argv[]) { std::cout << "hello world" << std::endl; return 0 ; }
命名空间
用来解决include两个库,但是两个库有同名同参同返回值的函数的冲突。只要使用namespace::function
来调用就可以避免冲突,相当于给函数加了前缀。
using namespace xxx
1 2 3 4 5 6 7 8 #include <print> int main (int argc, char * argv[]) { using namespace std; println ("hello world" ); return 0 ; }
使用using namespace std,使得该块剩下部分的函数如果找不到就优先指定为std中的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <print> void println (const char * p) { ; } int main (int argc, char * argv[]) { using namespace std; println ("hello world" ); return 0 ; }
该情况下优先调用自己的println
using x::y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <print> void println (const char * p) { ; } int main (int argc, char * argv[]) { using std::println; println ("hello world" ); return 0 ; }
使得之后块的y函数始终加x命名空间,此时println为std
上面两种同时使用时,using效果大于using namespace,两个都是using namespace时,需要转换规则。两个都是using时,using效果只针对到下一个using为止。using namespace不可以同时在全局使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <print> namespace b{ void println (const char * p) { ; } } int main (int argc, char * argv[]) { using std::println; println ("hello world" ); using b::println; println ("hello world" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 重载解析(Overload Resolution)决胜法则 即使 b::println 与 std::println 同时可见,编译器会根据下面原则选最优匹配: 普通函数优先:非模板函数优于函数模板 参数匹配度:精确匹配(const char * → const char *)优于模板推导或额外转换 在 C++23 <print> 中,std::println 通常是一个可变参函数模板,需要做模板推导;而 b::println (const char *) 则是完全精确匹配的普通函数。 结果:两次调用都选中了 b::println。
面向过程中和C不一样的部分
基本变量
添加bool类型:true和false
添加auto类型,自动识别变量类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <print> bool isZero (int d) { return d == 0 ; } int main (int argc, char * argv[]) { auto d = 0 ; if (isZero (d) == true ) std::println ("it is zero!" ); return 0 ; }
foreach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 std::vector<int > v{1 ,2 ,3 ,4 ,5 }; for (const auto & x : v) { std::cout << x << ' ' ; } std::map<std::string,int > m { {"apple" , 3 }, {"banana" , 5 }, {"cherry" , 2 } }; for (const auto & [key, value] : m) { std::cout << key << " -> " << value << "\n" ; } int a[2 ][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 }};for (auto & row : a) { for (auto x : row) { std::cout << x << ' ' ; } std::cout << '\n' ; }
用于快速遍历容器
基本内存分配
new和delete关键字用于分配动态内存
1 2 3 4 5 6 7 int * a = new int (3 );int * b = new char (4 );int * c = new int [10 ];delete a;delete b;delete [] c;
oprator new和oprator delete是函数,用于进行重载
额外指针类型与类型转换
空指针类型为std::nullptr_t。可以和任意类型非函数/类指针隐式转换。
于此对应的,void*不再自动隐式转换
同时定义了std::ptrdiff_t/std::intptr_t / std::uintptr_t来存放指针和差值,可以直接和值类型转换
1 2 3 int * a = new int (3 );int * b = new int (4 );std::ptrdiff_t diff = a - b;
const,auto,ptr,refer
最痛苦的一集。
const表示变量不可修改。
auto表示自动推断声明的变量类型。
*表示声明指针
引用
&表示声明引用。引用是变量的别名,比如:
1 2 3 4 int a = 1 ;int &b = a;b++; cout<<a;
左值引用是只能指向一个变量的指针
。即: int *const b = a;
eg:
1 2 3 4 5 6 7 8 9 10 11 12 void foo (int & a,int & b) { a = a+b; b = a-b; a = a-b; } int main (void ) { int a=0 ; int b=1 ; foo (a,b); foo (1 ,2 ); }
常量引用是带const的左值引用,不能用它来变更值。
eg:
1 2 3 4 5 6 7 8 9 void foo (const string& a) { cout<<a; } int main (void ) { string a ("hello" ) ; foo (a); foo ("world" ); }
右值引用只能绑定一个右值:int&& x = 1;
。也许是为了让函数可以传引用,又可以直接传值
eg:移动
1 2 std::string make_str () { return "temp" ; }std::string s = std::move (make_str ());
顶层底层const
const char为底层const
*const为顶层const
auto
1 2 3 int a =1 ;int &b =a;auto c = b;
忽略引用,需要时,显式声明
1 2 3 4 5 6 7 int a = 0 ;const int b = a,&c=b;auto k = b;auto l = c;auto m = &a;auto n = &b;auto o = &c;
忽略顶层const,需要时,显式声明
1 2 const int a = 0 ;auto & b = a;
如果auto有&,保持顶层const
如果原值为数组,推导指针,如果原值为数组&推导数组。
后置类型推导也会用auto推导函数返回值
1 2 3 4 template <typename T, typename U>auto add (T t, U u) -> decltype (t + u) { return t + u; }
类型转换
C风格:(char)a
CPP风格:char(a)
上面为隐式转换,会依次尝试下面几种显式找到合适的转换方式:
运算符
值
指针
对象
static_cast<T>(expr)
支持的隐式转换
void*到T*的转换,首先得是可相互转换的(包括指向类的指针和指向该类第一个非静态成员的指针)
用户定义转(父类到子类等)
const_cast<T>(expr)
移除表达式的const或增加表达式的volatile修饰,不能修改底层const
同左,函数指针不受影响
同左,成员函数指针不受影响
dynamic_cast<T>(expr)
无
沿继承结构向上/下/横向将指针/引用转换为类,可能需要运行时检查
无
reinterpret_cast<T>(expr)
自由转换,没有限制
自由转换,没有限制
自由转换,没有限制
异常处理
1 2 3 4 5 6 7 8 9 10 try { throw expr; }catch (T e1){ }catch (T e2){ }catch (...){ }
面向对象部分
类声明
1 2 3 4 5 6 class myclass { int a; float b; public : void change (void ) ; };
1 2 3 4 5 6 7 8 9 myclass::myclass (){int k = 0 ,float e = 0.0 }{ a = k; b = e; } myclass tt = myclass (1 ,1.0 ); myclass tt (1 ,1.0 ) ;myclass tt; myclass::~myclass (){}
如果构造函数支持一个参数,支持直接赋值:
classname object = value;
this指针
如果方法涉及到两个或以上的对象,则需要使用this指针。this指针指向调用这个方法的对象的地址
内部的缩写
a等价于this->a
要引用整个表达对象(对象的值)时,用*this,
多态
继承
1 2 3 4 5 6 7 8 9 10 class Person {public : std::string name; int age; }; class Student : public Person {public : std::string studentID; };
public表示共有还是共有,保护还是保护
protected表示共有变为保护,保护还是保护
private 共有/保护变为私有
多继承
1 2 3 4 5 6 7 class Printer { };class Scanner { };class MultiFunctionDevice : public Printer, public Scanner { };
虚继承
1 2 3 4 class A { };class B : virtual public A { };class C : virtual public A { };class D : public B, public C { };
虚函数
virtual声明关键字
1 2 3 4 5 class Base {public : virtual void foo1 (int ) ; virtual void foo2 (int ) ; };
1 2 3 4 5 6 7 8 class Derived : public Base {public : void foo1 (int ) override ; void foo2 (int ) const final ; };
如果含=0表示纯虚函数,必须在某个派生类提供定义,含有纯虚函数的类不能实例化,派生函数同理。
1 2 3 4 5 class Abstract {public : virtual void func () = 0 ; virtual ~Abstract () = default ; };
运算符重载
运算符重载是一种多态。C++根据操作数的数目和类型来决定采用哪种操作。
格式:
1 2 3 4 5 6 7 8 9 10 11 12 myclass operator +(const myclass& t) const ; myclass myclass::operator +(const myclass & t) const { myclass k; ... return k; } a = b.operator +(c); a = b + c;
重载的限制
重载后的运算符必须至少有一个操作数是用户定义的类型。
使用运算符时不能违反运算符原来的语法规则。(操作数相等,不能修改操作优先级)
不能创建新的运算符
不能重载
sizeof:sizeof运算符
.:成员运算符
.*:成员指针运算符
:::作用域解析运算符
?::条件运算符
typeeid:一个RTTI运算符
const_cast:强制类型转换运算符
dynamic_cast:强制类型转换运算符
reterpret_cast:强制类型转换运算符
stati_cast:强制类型转换运算符
只能通过成员函数重载的运算符
=:赋值运算符
():函数调用运算符
[]:下标运算符
->:通过指针访问类成员的运算符
lambda表达式和函数式编程
在面向对象中用表达式声明和定义一个函数,使其可以捕获上层函数的变量,实现闭包。或是定义只能在某个函数中使用的匿名函数
语法
1 [capture list] (parameter list) [mutable/exception /attribute] -> return type { function body }
capture list:捕获列表
值捕获,定义时直接传值
引用捕获,内部使用的是对应变量的引用
隐式捕获,捕获所有外部变量,=表示值捕获,&表示引用捕获
初始化捕获,捕获列表中可以定义变量,且可以自动auto
parameter list:传入参数列表,同普通的函数
[mutable/exception/attribute]:不加mutable会自动给按值捕获的变量加const
type:返回类型,省略后自动推导
function body:函数体
举例
自定义排序算法
1 sort (arr,[](int a,int b){return a>b;});
auto泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int main (void ) { auto f = [] (auto x) -> auto { if (is_integral<decltype (x)>::value) { return x * 2 ; } else if (is_floating_point<decltype (x)>::value) { return x / 2 ; } else { return x; } }; }
捕获类中的成员
1 2 3 4 5 6 class A { int num; void test () { auto func = [*this ](){return 2 *num;}; } }
智能指针
自动管理内存的指针,不需要手动释放
1 2 3 4 5 6 7 8 #include <memory> void demo () { std::unique_ptr<int > p1 = std::make_unique <int >(42 ); *p1 = 100 ; auto p2 = std::move (p1); }
unique指针不能复制,只能移动
1 2 3 4 5 6 7 8 9 10 11 12 #include <memory> #include <iostream> void demo () { auto p1 = std::make_shared <int >(55 ); std::cout << p1.use_count (); { auto p2 = p1; std::cout << p1.use_count (); } }
shared指针自动计数并删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <memory> #include <iostream> void observe (std::weak_ptr<int > wp) { if (auto sp = wp.lock ()) { std::cout << *sp; } else { std::cout << "expired" ; } } int main () { std::weak_ptr<int > wp; { auto sp = std::make_shared <int >(10 ); wp = sp; observe (wp); } observe (wp); }
weak指针,关联一个shared_ptr
移动语义与完美转发
move的函数定义:
1 2 3 4 template <class _Ty >remove_reference_t <_Ty>&& move (_Ty&& _Arg) { return static_cast <remove_reference_t <_Ty>&&>(_Arg); }
遵循折叠规则:
组合形式
折叠后结果
&
与 &
&
&
与 &&
&
&&
与 &
&
&&
与 &&
&&
泛型与模板
1 2 3 4 template <class T1 >auto move (T1 x,int y) { return x+y; }
自动编译成多个函数,只要使用了不同的类型就有一个
std库
C++ 标准库 | 菜鸟教程