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. 面向对象程序设计与原则

3. C++进阶-STL容器,你看我就够了

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++并发编程(中文版)

posted @ 2018-09-09 14:16  yuxi_o  阅读(356)  评论(0编辑  收藏  举报