• C++ 基础知识(一)


    1.#inclue
    #inclue<iostream>中的 # 是预处理标志,要最先处理,即在编译器编译代码之前运行
    #inclue<>   和 #include “” 的区别:

    < >引用的是编译器的类库路径里面的头文件
    " "引用的是你程序目录的相对路径中的头文件
    < >一般是引用自带的一些头文件:stdio.h、conio.h、string.h、stdlib.h等等之类的
    " "一般是用来引用自己写的一些头文件
    如果使用" ",会先在你项目的当前目录查找是否有对应头文件
    如果没有,它还是会在对应的类库路径目录里面查找对应的头文件


    2.iostream是一个标准库,里面包含了cin和cout,用法就是std::cout<<"hello";
    int x;
    std:cin>>x;

    还可以这样:
    int a,b,c;
    std::cin>>a>>b>>c;
    std::cout<<a<<" "<<b<<" "<<c<<"ddd";
    " "代表换行,和std:endl是一个意思,但是后者还会刷新缓存区,两者基本一样

    3.std是标准的意思。
    namespace是命名空间。
    <iostream>是头文件。//注意iostream.h
    cout,cin,endl是在std命名空间中定义的对象。

    4.C++中cout定义在命名空间std中,因此,在使用cout时可以用std::cout这样的方式使用,需要注意的是,cout是ostream类型的对象,而ostream类型定义在iostream头文件中,因此,在使用cout时,还需要使用#include<iostream>. 总结:C++中的命名空间和C#中的意思基本一样,也是相同的作用,用法也差不多,C++中的std是包含了所有的标识符的命名空间,所以一般都要using std,当然也可以直接std::cout 这样调用。

    5.system("pause");是暂停的意思,在main函数最后可以不让控制台关闭,用Ctrl+F5也不会关闭,F5调试的话又不加system("pause")的话就会自动关闭。

    6.#include "stdafx.h" ???

    7.#include<iostream>和<iostream.h>的区别:
    #include<iostream>中的iostream是C++标准头文件库,而#include<iostream.h>是C中的头文件库,因为C++继承了C的特性,所以也保留了iostream.h这种写法,因为C++引入了namespace,所以用iostream的时候就要开辟命名空间,例如:
    #include<iostream>
    using namespace std;
    用iostream.h的时候就不用命名空间;


    8.C++的编译器是从上到下的,例如这段代码
    int _tmain()
    {
        std::cout <<add(6,9);
        return 0;
    }
    int add(int x, int y)
    {
        return x + y;
    }
    编译器会报错,但是把add放在上面就不会出错,C++的函数可以不用声明直接定义,但是这不是一个好习惯,最好先声明,原因很简单,声明后编译器就知道这个函数的存在了。声明的时候没有给函数分配内存,而定义就会函数分配内存



    9.class类的成员函数的声明和定义不建议全部放在类内,最好定义要放在外面,要加上域符号::
    为什么要声明和定义分开?因为写在一起就比如void p(){std::cout“111”;};
    这样一个函数其实被称为内联函数,就是相当于把函数实现部分拷贝到调用函数的地方,但是一旦{}里面的代码很大,拷贝就会浪费性能,所以声明和定义最好分开,这样就不会去拷贝了

    10.头文件用<>包括表示绝对路径,用""表示相对路径,include头文件就可以认为cpp文件包含了头文件中的所有代码。
    为什么要用头文件的原因:
    (1).可以被多个cpp文件同事引用,(2).类的使用者不关心类的细节,只要阅读头文件就可以知道所有信息


    11.析构函数和构造函数都不能有返回值,析构函数不能带参数,一个类只能有一个析构函数,而构造函数可以重载,可以带参数。

    12.析构函数会在程序结束时执行

    13.int *p;    p = 0;  第二句是将p变为空指针; 这样更安全


    14.double *p;这是一个double类型的指针,所以这个指针的内存为8个字节,
    int *q 这是一个int类型的指针,内存为4字节,而且两者不能相互转换
    指针的类型必须与变量的类型相匹配,不然会报错

    15.int a = 1;
        int* p;  //int类型指针
        p = &a;  
        cout << *p<<endl; //取指针代表的地址的值,即a的值为1
        cout << p << endl;; //指针p的值 为0057fe10
        cout << &p<<endl;   //指针的地址 0057fe04
        cout << &a << endl;  //a变量的地址,即p的值 0057fe10


    16.typedef 的意思就是把。。替换成。。;
    double a=9.0 ; double *p=&a;  *p就代表去取指针存放的地址所指向的值,即*p=9.0;


    17.为要使用指针:
    1.处理堆中存放的大型数据
    2.快速访问类的成员数据和函数
    3.以别名的方式向函数传递参数

    18.指针*号靠近变量还是靠近类型:
    int   *a;
    int*   a;
    首先这两种其实是一样的,无伤大雅,但更建议使用第一种,靠近变量
    两者意思相同且后者看上去更为清楚:a被声明为类型为 int* 的指针. 但是,这并不是一个好技巧,原因如下:
    int* b, c, d;
    人们很自然地以为这条语句把所有三个变量声明为指向整形的指针, 但事实上并非如此. 我们被它的形式愚弄了. 星号实际上是表达式 *b 的一部分, 只对这个标识符有用. b 是一个指针, 但其余两个变量只是普通的整形. 要声明三个指针, 正确的语句如下:
    int *b, *c, *d;

    19.引用&
    int  &ra=a; 引用就是别名,就象库里的小名叫小学生,两者都是指库里
    C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。
    引用就是常量,只能进行初始化,不能赋值  即int &ra=a;而不能写成 int &ra;ra=a;
    引用也可以是类的引用,即代表该类的别名
    引用的自身的地址和本尊是一样的,只能修改其保存的值,而不能修改本身

    引用很容易与指针混淆,它们之间有三个主要的不同:
    1.不存在空引用。引用必须连接到一块合法的内存。
    2.一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
    3.引用必须在创建时被初始化。指针可以在任何时间被初始化。


    20.传参三种方式:
    int a=3;int b=4;
    (1)按值传递  void swap(int a,int b);传进去之后不改变原来函数外a和b的值,跟C#一样,按值传递会在函数内部创建一个对象的副本,返回的也是一个对象的副本,所以外面的值不会改变.
    (2)按地址传递   void swap(int *a,int *b);传入后会改变原来a和b的值
    (3)按引用(别名)传递   void swap(int &a,int &b);传入后会改变原来a和b的值,这个&就相当于C#中的ref
    尽量使用按别名传递的方式,这样能解决创建副本的问题,也能节省性能

    下面的例子,会输出
    a=11 b=22
    a=22 b=11
    a=22 b=11

    #include<iostream>
    void swap0(int a, int b) //按值传递
    {
    	int temp = a;
    	a = b;
    	b = a;
    }
    
    void swap1(int& a, int& b)//按引用传递
    {
    	int temp = a;
    	a = b;
    	b = temp;
    }
    
    void swap2(int* a, int *b)//按地址传递
    {
    	int temp = *a;
    	*a = *b;
    	*b = temp;
    }
    
    int main()
    {
    	int a = 11; int b = 22;
    	swap0(a, b);
    	std::cout << "a=" << a << " " << "b=" << b << std::endl;
    
    	a = 11; b = 22;
    	swap1(a, b);
    	std::cout << "a=" << a << " " << "b=" << b << std::endl;
    
    	a = 11; b = 22;
    	swap2(&a, &b);
    	std::cout << "a=" << a << " " << "b=" << b << std::endl;
    	return 0;
    }

    21.c++为什么要产生副本?通过汇编理解指针,学习汇编。。

    22.栈区和堆区
    1.栈区:由编译器自动分配并且自动释放,一般是函数参数和局部变量
    2.堆区:一般有程序员分配释放,若不释放,则程序结束时由系统释放
    堆是有一串不连续的链表串联起来的内存,速度较慢,容易产生内存碎片,容易使用,在申请内存的时候,系统会遍历一个空闲地址的链表,找到合适的地址分配给使用者
    栈是系统自动分配的,程序员不能操作,而且一般较小,一般是函数参数或者局部变量,当函数结束时会释放,栈中的内存是先进后出的。一般第一个进栈的是函数下一行的内存地址,然后从右到左的函数参数,然后是变量,出栈时完全相反,所有变量出栈之后就剩函数下一行的内存地址了,然后自动跳转到下一行执行!


    23.内存的三种分配方式:
    1. 从静态存储区分配:此时的内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在。全局变量,static变量等在此存储。
    2. 在栈区分配:相关代码执行时创建,执行结束时被自动释放。局部变量在此存储。栈内存分配运算内置于处理器的指令集中,效率高,但容量有限。
    3. 在堆区分配:动态分配内存。用new/malloc时开辟,delete/free时释放。生存期由用户指定,灵活。但有内存泄露等问题。

    24.类的初始化,用new和不用new的区别:
    通过下面这个例子,会输出“释放1”,我们可以得出在不使用new创建对象时,对象的内存空间是在栈中的,其作用范围只是在函数内部,函数执行完成后就会调用析构函数,删除该对象。而使用new创建对象是创建在堆中的,必须要程序员手动的去管理该对象的内存空间

    #include<iostream>
    class TestNew
    {
    public:
    	TestNew(int ID);
    	~TestNew();
    private:
    	int ID;
    };
    
    TestNew::TestNew(int ID)
    {
    	this->ID = ID;//this就是TestNew类
    }
    
    TestNew::~TestNew()
    {
    	std::cout << "释放" << this->ID << std::endl;
    }
    
    void Test()
    {
    	TestNew test(1);
    	TestNew * ptest = new TestNew(2);
    }
    
    int main()
    {
        Test();
        return 0;
    }


    25.int *p=new int; 在堆中new一个int型 即4字节的内存空间,内存地址保存在p中
    *p=3,     修改这块内存中存放的值,改为3
    delete p;  释放这块内存空间,但是并没有释放p,p指针还能继续使用,
    p=0;      一般是将p清0,以免错误
    p=new int;   p还能继续使用
    *p=400;       修改这块内存中存放的值,改为400


    26.内存泄漏:因为new在堆中的内存不会自动释放,假如保存这块内存的指针是一个函数中的局部变量,当函数执行完毕后,会释放这个指针,那么这块内存就象消失了一样,再也找不回来了,这就叫内存泄漏,必须对这个指针使用delete

    27.常见内存错误及对策
    1. 内存分配未成功,却被使用。
    对策:使用内存之前检查是否分配成功。用p!=NULL判断。
    2. 内存分配成功,未初始化就被使用。
    内存的缺省值没有统一的标准。大部分编译器以0作为初始值,但不完全是。
    对策:内存初始化时赋初值。
    3. 内存操作越界。
    对策:只能是小心了。
    4. 释放了内存,仍然使用。
    (1) 使用显示delete和free的野指针。
    对策:释放完内存,将指针置为NULL。
    (2) 使用隐式delete和free的野指针。主要是指函数返回指向栈内存的指针或引用。
    对策:当然是不要返回就可以了。
    5. 未释放内存,导致内存泄露。
    用new/malloc开辟了内存,没用delete/free释放.
    对策:new和delete的个数一定相同;malloc和free的个数一定相同;new[]和[]delete一定对应

    28.指针能加减能比较大小


    29.将指针定义为常量指针 int *const p=&a;  p是一个常量指针,p本身的值无法修改,但是它指向的地址可以修改

    30.假如把const放在前面  const int *p=&a;   p是指向常量的指针,p本身可以修改,但是p所指向的目标不能修改

    31.函数的成员变量的初始化

    class A
    {
    public:
       A():x(4),y(7)  //这是一种成员变量的初始化写法,会输出28
       {
        cout<<x*y
       };  
    private:
       int x;
       int y;
    }

    如果把x定义为const的话,就只能使用这种写法给成员变量初始化,因为常量和引用只能被初始化不能被赋值。

     

  • 相关阅读:
    Zephyr网络协议
    Zephyr启动过程与中断响应
    CortexM处理器的一些特性记录
    qemu 使用记录
    Request与Response详解
    http请求头(Header)参数详解
    win10java环境变量配置
    逻辑运算符:与、或、非、异或
    SQL注入相关知识整理
    网页是否存在SQL注入的简单判断
  • 原文地址:https://www.cnblogs.com/kevinWu7/p/10163513.html
Copyright © 2020-2023  润新知