C++学习笔记

Posted 木大白易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++学习笔记相关的知识,希望对你有一定的参考价值。

本文来自菜鸟教程的总结。

指针

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
一般形式为type *name
type为指针的基类型,name为指针变量的名字,*用来指示这个变量是指针。

int  var = 20;   // 实际变量的声明
int  *ip;        // 指针变量的声明
 
ip = &var;       // 在指针变量中存储 var 的地址

//打印相关变量的值
//var----->20         表示变量var
//ip----->0xbfc601ac  表示指向变量var的内存地址
//*ip----->20         表示指向变量var内存地址上的值

NULL指针

在指针初始化或者手动销毁的时候,通常赋值为NULL 。
指向指针的指针
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。
指针的指针就是将指针的地址存放在另一个指针里面。

    int  var;
    int  *ptr;
    int  **pptr;
 
    var = 3000;
 
    // 获取 var 的地址
    ptr = &var;
 
    // 使用运算符 & 获取 ptr 的地址
    pptr = &ptr;
    
    //那么var,*ptr,**pptr的值是一样的,都是3000

传递指针给函数

传递指针给函数,只需要声明函数参数为指针类型。

void passPtr(int *p);//声明函数
void passPtr(int *p){//函数定义
    *p = 2021;
    return;
}

int main(){
    int a = 2020;
    passPtr(&a);  //函数将a的值改为了2021
    cout << a << endl;
    return 0;
}

引用

注意它和指针的区别:

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。
    //引用
    int i = 5;//声明简单的变量
    int& r = i;//声明引用变量

    cout << i << endl;
    cout << r << endl;
    
    //i和r打印的值都是5,因为都是一块内存

数据结构

//声明一个结构体类型 User
struct User{
    char name[20];
    int age;
};

User zhangsan;//定义结构体类型为User的变量zhangsan

//赋值
strcpy(zhangsan.name, "张三");
zhangsan.age = 18;

类和对象

跟java类似

class User{
    private:  //访问修饰符后边要有一个冒号
        string name;
        int age;

    public: 
        void setName(string p_name){  //类成员函数
            name = p_name;
        } 
        string getName(){
            return name;
        }
};  //这里注意要有一个分号

int main(){
    User zhangsan;
    zhangsan.setName("张三");
    cout << "zhangsan的名字:" << zhangsan.getName() << endl;
    return 0;
}

类成员函数除了可以在类内部定义,也可以在外部:

//在类内部声明,在类外部定义
void User::setName(string p_name){  //类名::成员函数名
    name = p_name;
}

构造函数和析构函数

当一个对象被创建的时候,构造函数被执行。
析构函数在每次删除对象时执行。

class User{
    private:  //访问修饰符后边要有一个冒号
        string name;
        int age;

    public: 
        User(){} //无参构造函数
        User(int p_Age){ //有参构造函数
            age = p_Age;
        }
        
        ~User(){ //析构函数
            cout << "对象销毁" << endl;
        }  
};

静态成员和静态函数

使用static关键字修饰
调用的时候:

类名::静态变量;
类名::静态函数();

继承

子类可以访问父类所有非私有的成员和函数。

class Animal{
    public:
        void eat(){}
        void sleep(){}
    private:
        void mySecret(){}
};

class Dog : public Animal{ //继承  访问修饰符默认为private
    public:
        void bark(){}
};

另外,继承类型由访问修饰符控制,默认为private。
public:父类public,protected成员和函数仍为子类的,类型不变。private类型不能由子类直接访问。
protected:父类public,protected成员和函数为子类的protected类型。
private:父类public,protected成员和函数为子类的private类型。子类不能直接访问。

函数重载和运算符重载

函数重载:可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。与返回值无关。
运算符重载:重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。

User operator+(const User&);//对加号+重载,将两个User对象相加并返回一个User对象

多态

当一个子类继承父类,要重写父类某一个函数时,父类中函数要使用virtual关键字修饰,否则子类调用函数被编译器设置为父类中的版本,这就是所谓的静态多态。
在父类中使用关键字virtual声明的函数,被称之为虚函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。这种操作被称为动态链接。
如果在父类中定义虚函数,但是又不想给具体的函数实现,使用=0表示函数没有主体,这样的虚函数被称之为纯虚函数。

class User{
    virtual string work() = 0;//纯虚函数
}

如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。比如上边的User类现在就是一个抽象类。
抽象类不能被实例化对象,它只能被作为接口使用。所有继承抽象类的子类如果要被实例化,则必须要实现抽象类中的所有纯虚函数。

动态内存

new,delete

int* p = NULL; //声明变量
p = new int; //在堆中分配内存
*p = 2021; //赋值

delete p; //释放内存

它与int p = 2021的区别就是这种是在栈内存中的,使用完会自动释放。而动态分配内存是在堆中,需要手动释放。

数组的动态分配

// 动态分配,数组长度为 m
int *array=new int [m];
 //释放内存
delete [] array;

模板

类似于java的泛型。

函数模板

template <typename type> ret-type func-name(parameter list)
{
   // 函数的主体
}


template <typename T> T max(T a, T b){
}

类模板

template <class type> class class-name {
    //类主体。。。
}


template <class T> class Stack { 
  private: 
    vector<T> elems;     // 元素 
 
  public: 
    void push(T const&);  // 入栈
    void pop();               // 出栈
    T top() const;            // 返回栈顶元素
    bool empty() const{       // 如果为空则返回真。
        return elems.empty(); 
    } }; 

预处理器

预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。
所有的预处理器指令都是以#开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号;结尾。

#define
#define 预处理指令用于创建符号常量。该符号常量通常称为宏。
#define PI 3.14  
//这里注意后边的括号
#define MIN(a,b) (a<b ? a : b)    

条件编译

条件预处理器的结构与 if 选择结构很像。
#if 0表示注释掉代码,不进行编译。

#define DEBUG

int main ()
{
#ifdef DEBUG
   cerr <<"Trace: Inside main function" << endl;
#endif

#if 0
   /* 这是注释部分 */
   cout << MKSTR(HELLO C++) << endl;
#endif
    return 0;
}

预定义宏

  • LINE:当前行号
  • FILE:当前文件名
  • DATE:当前月 日 年的日期
  • TIME:当前h:m:s的时间

多线程

创建线程

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 

pthread_create 创建一个新的线程,并让它可执行。

  • thread:线程指针,类型为pthread_t
  • attr:设置线程属性,可以为NULL
  • start_routine:函数指针,在线程被创建时调用(比如void *startFunc(void *arg))
  • arg:上边运行函数的参数,用于向线程传递参数

终止线程

#include <pthread.h>
pthread_exit (status) 

pthread_exit 用于显式地退出一个线程。

连接和分离线程

pthread_join (threadid, status) 
pthread_detach (threadid) 

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。
如果不想被阻塞,可以在子线程中加入代码 pthread_detach(pthread_self())或者父线程调用 pthread_detach(thread_id)(非阻塞,可立即返回),这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。

以上是关于C++学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

《C++ Primer Plus》学习笔记——C++程序创建到运行的整个过程

C++ 学习笔记

C++ 解释器/控制台/片段编译器

C++代码调试的学习笔记

C++学习笔记1

C++异常处理的学习笔记