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++ new
和delete
操作符
new 型;
创建
以下各行所有写法等效
【...】
可以不写,表示初始化
- C++:
型 *针=new 型【(值)】; //new的返回地址会自动转换成(型*)
// <=>
型 &名=*(new 型), *针=&名;
- C:
型 *针=(型*)malloc(sizeof(型));【*名=值;】 //new的返回地址是void*,不会自动转换成(型*)
// <=>
型 &名=*(型*) malloc (sizeof(型)*n),*针=&名;
// <=>
型 名【=值】; 型 *针; 针=&名;
// <=>
型 名【=值】; 型 *针=&名;
释放
以下各行所有写法等效
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)
释放内存,即free
、delete
、delete[]
,只能告诉操作系统哪些指针所指的存储空间已经闲置,可做别用;但不能废除这个针,针的存储值没变,*针还是可以访问原来所指的位置,因此为了避免后面出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++ 类 和new
、delete
操作符
能调用构造函数:new
,类 对象;
能调用析构函数:delete
,delete[]
不能调用构造函数:malloc
、类 *针;
不能调用析构函数:free
2.13 C++ 友元函数和友元类
-
友元函数的申明可以写在类体力的任意位置,
public
、protected
、private
下均可,一般写在类体的开头 -
友元函数的申明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’
-
-
string
、const 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