c++入门笔记

批注教程

1.4 C++ 命名空间(名字空间)

  • namespace XX{xxxx}外定义的命名,遵循C语言的命名规范。

    重名时优先级别:内部变量>外部变量>用using设置默认的变量>XX::xxx

  • 一个cpp文件中using只能写一次;可以写在函数内部,也可以写在函数外部;不可再函数外写了一次,又在函数内再写。

1.5 C++ 标准库和std 命名空间

所有d的库库名.h在C++中写作c库名,前者不在std中,后者在。二库略异。

必须在std下用的:

cin cout
string

1.7 C++ 新增数据类型

bool型变量只能取0或1,

  • 它参与运算时与int性质相同
  • 对它赋值时,非0值全部转换为1,0转换为0
  • 非bool值不会自动转换成bool值,需要用强制转换标记,转化原则同b.

bool a=1,b;
printf(%d”,a+1);//输出的是2
b=a+1;//a+1算出来是2,b赋值为1
a=0;//可以给bool值赋值0、1、false、true
int c=123;
a=(bool)c;//a被赋值成1
printf(%d”,a);//输出的是1

1.8 C++ newdelete操作符

new 型;

创建

以下各行所有写法等效

【...】可以不写,表示初始化

  • C++:
*=new 型【();  //new的返回地址会自动转换成(型*)
// <=>&=*(new), *=&;
  • C:
*=(*)malloc(sizeof());*=值;】 //new的返回地址是void*,不会自动转换成(型*)
// <=>&=*(*) malloc (sizeof()*n),*=&;
// <=>
型 名【=值】;*;=&;
// <=>
型 名【=值】;*=&;
释放

以下各行所有写法等效

  • C++
delete;
// <=>
delete &;
  • C
// <=>
free();
// <=>
free(&);

new 型[n];

创建

以下各行所有写法等效

  • C++
*=new[n];
// <=>&=(new[n]),=&;
  • C:
// <=>
型 针=()malloc(sizeof()*n);
// <=>&=() malloc (sizeof()n),=&;
// <=>
型 名[n];*;=&;
// <=>
型 名[n];*=&;
释放

以下各行所有写法等效

  • C++:
delete[];
// <=>
delete[] &;
// `delete 针;` 只释放了(针),没释放(针+1),(针+2)…,(针+n-1)
  • C:
// <=>
free();
// <=>
free(&);
//皆释放了 针,(针+1),(针+2),…, (针+n-1)

释放内存,即freedeletedelete[],只能告诉操作系统哪些指针所指的存储空间已经闲置,可做别用;但不能废除这个针,针的存储值没变,*针还是可以访问原来所指的位置,因此为了避免后面出bug,在释放内存后,宜

=NULL;//或=0;

realloc比较

new/delete系列里没有与realloc同功能的函数

1.9 C++ 函数的默认参数

  • 不可以用”,”表示参数默认,如

    int f(int a,int b=1,int c=2)
    {
        cout<<a*b*c<<endl;
        return 0;
    }
    int main()
    {
        f(1,,1);//或f(1,default,1);				//这样是错的
        retun 0;
    }
  • 仅当所用参数的签名不同时,才不能重载,如:

    int f(int a,int b=1)void f(int a,int b)不可重载

  • 当非默认参数的签名(型的序列)不同,全部参数签名不同时,可以重载,但不可缺省默认参数调用,如:

    int f(int a,int b=1)int f(int a,double b=1)可重载,但不可用f(1)调用;

    int f(int a,int b=1)int f(int a) 重载,不可用f(1)调用;

    int f(int a,int b=1)与int f(int a,double b) 重载,可用f(1)调用;

    int f(int a,int b=1)与int f(double a,int b=1) 重载,不可用f(1)调用;

  • 若要让函数声明后,函数定义前,能够调用默认参数,则应在声明处设置默认默认参数,而定义处不得再次声明。一般为避免混淆,宜一律在函数声明(如头文件中)设置默认参数,函数定义时不设置默认参数。

2.6 C++ 构造函数

  • 构造函数与析构函数如

    是public :在任何处 可以定义或销毁该类的对象

    是protected :在子类内 可以定义或销毁该类的对象

    是private :在该类内 可以定义或销毁该类的对象

    详见

  • 复制构造函数

    • 自己不写类(const 类 &that)形式的构造函数<=>编译器会默认重载一个起到位拷贝(即浅拷贝)的构造函数,即

      ::(const&that)
      {
          this->变量1=that.变量1;
          this->变量2=that.变量2;}
    • 故若有变量是指针,拷贝完后,新对象与旧对象的该变量指向同一个地址,因此两个对象相互不独立。如果要深拷贝,则需自己写深拷贝的拷贝函数:

      ::(const&that)
      {
          this->变量1=that.变量1;			//变量1不是指针
          *(this->变量2)=*(that.变量2);	//变量2是指针}
    • 空构造函数:自己不写构造函数(可以有任何变量签名,包括自己写的复制构造函数)ó编译器会生成一个空构造函数

      ::(){};

2.10 C++ this 指针详解

在调用一个对象的成员函数时,开辟this指向该对象,成员函数结束后,销毁this。故:

class{
    public:
    void f(){
        cout<<this<<endl;
    }
};
类 对象1;
类 对象2;		//此时未调用对象2的函数,故没有this
对象1.f();		//输出的是对象1,而非2的地址

2.11 C++ static静态成员变量和静态成员函数

网站教材勘误:

初始化

  • static 变量只能在函数外,以如下格式申明后,后面的行才能使用:

    (static) 型 类::变量(=初值);//不写初值则默认初值为0,即static的通性

    故建议在类定义完后,紧接着写申明。

  • 在函数外,只能初始化,不能访问(因为C/C++中,在函数外,不能执行命令,赋值是命令,定义变量、申明、初始化不是)),即不可以在函数外:

    (static) 型 类::变量;//初值为0
    型 类::变量=;//这里出错
  • static 成员变量的内存空间既不是在声明类时分配,也不是在创建对象时分配,而是在初始化时分配。static 成员变量与对象无关,不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以在可访处访问。

访问

static变量是public可以任处在里访问,private只能在类里访问,protected只能在子类里访问。

初始化后才能访问(包括写入或读取,如赋值、输出、拷贝),包括以下两种格式:

::变量
对象::变量

2.12 C++ 类 和newdelete 操作符

能调用构造函数:new类 对象;

能调用析构函数:deletedelete[]

不能调用构造函数:malloc类 *针;

不能调用析构函数:free

2.13 C++ 友元函数和友元类

  • 友元函数的申明可以写在类体力的任意位置,publicprotectedprivate下均可,一般写在类体的开头

  • 友元函数的申明friend不可省略,不然会被认为是成员函数

  • 友元函数的自变量建议用引用,以节省复制对象的时间。即:

    • 需要更改对象成员变量时用类 &

      friend 型 函数(&);		//申明
      型 函数(&引用名){}	//定义
    • 不需要更改对象成员变量时用“const 类 &”,以保护被引用对象不被破坏

      friend 型 函数(const&);		//申明
      型 函数(const&引用名){}	//定义

2.15 类class 和结构体struct 的区别

sturct

  • C: 没有private/protected/public, 没有成员函数, 友元,析构/构造
  • C++: 与class完全相同,仅一个区别:struct默认是public, class默认是private

3.2 C++ 继承权限和继承方式

改变成员函数的访问属性,格式为:

using 基类::成员函数名;

3.5 派生类的构造函数

如在派生类的构造函数中,未调用基类的构造函数,则会自动调用基类中不带参数的构造函数,即

基类(){}
  • 若又在基类中没有自己写的构造函数,则会调用自动补全的空构造函数,即
基类(){}
  • 若又在基类中有自己写的构造函数,则不会自动补齐的空构造函数。若还又基类中自己没有写不带参数的构造函数,则派生类无法调用基类构造函数,故无法构造派生类对象,因此编译不通过。

3.10 C++ 基类和派生类的赋值

对象赋值和指针赋值,派生类可以赋给基类,反之不可。

4.1 C++多态的概念及前提条件

  • 指针调用哪个类的函数,与指针访问哪个类的数据,这两个类不必相同,辨别方法如下:

    • 函数之类(除了虚函数,它与数据之类相同):

      定义指针:函数之类 *针;

      强制转换:(函数之类 *)针->函数之类的成员函数; 针定义时的类型可以是void *、函数之类、函数之类的父类或子类;不必与数据的类匹配,如果函数之类要访问数据之类没有的成员数据(如数据之类是函数之类的祖先类,而函数要访问函数之类新加的数据),仍然不会报错,而是会按照函数之类的数据存储方式去读取数据,这很不安全。

    • 数据之类:

      初始化:函数之类 *针=new 数据的类;

      赋值:针=对象; 对象的类是定义针的类及其子孙类;数据之类变为对象的类

  • 虚函数的virtual只可在类体内(的声明或者定义)写,不可写在类体外的(如函数定义)前

  • 只需在基类(父类)中的被覆盖的函数前写“virtual”,在子孙类(派生类、派生类的派生类……)的覆盖函数前可不写,可以实现:

    成员函数为父类或子孙类,成员数据为函数之类的子孙类,用调用虚函数,调用的是数据之类的,如:

    定义:

    class A
    {
    public:
        virtual void f(){cout<<"A"<<endl;}
    };
    
    class B:public A
    {
    public:
        void f(){cout<<"B"<<endl;}
    };
    
    class C:public B
    {
    public:
        void f(){cout<<c<<"C"<<endl;}
    };

    然后:

    • A *p=new A;
      p=new B;
      p->f()//输出‘B’
      
    B *p=new B;
    P=new C;
    p->f()//输出‘C’

C/C++ 中的字符串

  • 现在的C/C++可以

    const char *string="哈哈哈哈";
    const char *string;
    string="哈哈哈";
    const char *string=new char;
    string="哈哈哈";
    const char *string=new char(‘s’);
    string="哈哈哈";
    const char *string=new char[10];
    string="哈哈哈";

    以上五者对应的计算机的操作相同:在const char *string,时开辟内存存储指针string,在赋值*string="哈哈哈"时,另开辟内存存储该字符串,并让string指向该字符串的首字符。在赋值代码中"哈哈哈"是const char *`

    以上三者的const均不能省去,古老的C语言编译器才能如此,现在的编译器会报错:“warning: conversion from string literal to 'char *' is deprecated”。

    const表示一旦赋值后,不得更改string所指的字符串的字符,可以更改string指向新的字符串。

  • 可以在定义字符串数组时赋值

    char string[]="哈哈哈";
    char string[10]"哈哈哈";

    不可以

    char string[]; //因为机器不知道开辟多大的连续内存给数组string
    char string[10];//开辟一连片10个char长度的内存,但未经初始化;指针string指向这片内存的首字符。
    string="哈哈哈";//这句出错,因为:[数组名] 是一种特殊的指针,在定义它时的指向所定义的数组,之后[不能更改其所指]
  • #include <iostream>
    using namespace std;

    后,可以使用string型的变量,它是char *型的变量,可以如下操作:

    • 定义:

      string 串;
      //定义时开辟一个空间给串,串未初始化,此时输出string的结果是没有字符="哈哈哈";
      //若串后方的可用空间够,则从串+1开始储   存字符串,string首存地址&串[0];若串后方的可用空间不够,则另寻找够的地方存字符串,string首存&串[0]
      string 串="哈哈哈";
      • 可以用串[n]表示串的第n个字符,可修改,故串不是const char *,如:
      [n]='a';
    • 输出:

      输出字符串成员:

      printf("%c",[2]);
      cout<<[2];

      输出整个字符串:直接输出整个字符串,见’\0’结束:

      cout<<;
      cout<<&[0];
      printf("%s",&[0]);

      从中间开始输出字符串:

      printf(""%s",&[2]);
      cout<<&[2];

      输出地址:

      printf("%p",);
      printf("%p",);
    • 输入

      • 法一
      cin>>;
      //或
      fstream 文件;//ifstream亦可,均要#include<fstream>

      文件<<串;//从第一个非空格开始接受,见空格结束,残余的键入字符在键盘缓冲中

      • 法二
      getline(cin,);
      //或
      fstream 文件; //ifstream亦可,均要#include<fstream>getline(文件,);//从键盘缓冲第一个字符开始接受,到’\n’
  • stringconst char* 变量可以整体拷贝,仅有一下方法拷贝:

    string a,b="asda";b=a;
    string a,b="asda";b=&a[0];
    const char *a,*b="asda";b=a;
    const char *a;string b="asda";b=a;
    string a;const char *b="asda";a=&b[0];

    拷贝的原理:从字符串b的第一个字到第一个‘\0’(含)赋值,然后存到a存储区,故a,b的字符串是分开储存的。

  • 在用“XXXX”进行初始化和赋值时,可以写成

    =
    "aaa"
    "bbbb"
    "cccc";
    // <=>=
    "aaabbbbcccc";

5 宏定义

用公式进行宏定义,一下位置要加():

  • 需要表达式最外围用()

    不然调用时,直接文本替换就没有括号,如

    #define A 100
    #define B A+2//没括号int a=5*B;//替换为5*A+2,而不是5*(A+2)
    #define A 100
    #define B (A+2)//加括号int a=5*B;//替换为5*(A+2)
  • 所有做乘除法的宏量要加()

    正确写法例如:

    #define A 10000
    #define B ((A)*12)
    #define C (A+B)

6 const 的结合性质

仅当const在开头时可以右结合,在其余位置皆是左结合

  • 常型:const char <=> char const

  • 常型之针:const char* <=> (const char)*<=> (char const)*<=>char const *

  • 型之常针:char * const (不可对*(),故没有char (*const )

  • 常型之常针:常型之针后加const

  • 常函数:型 f(型 变量)const