C入门C++ 基础笔记

头文件

C++ 头文件不必是 .h 结尾,C语言中的标准库头文件如 math.h, stdio.h,这两个头文件在C++中被命名为 cmath, cstdio 。

#include <cmath>
#include <cstdio>

int main(){
	...
}

注释

多行注释 & 单行注释

/*
	C的多行注释
	用于注释一整块代码
*/
#include <cmath>
#include <cstdio>

int main(){   //程序执行入口
	double a = 1.2;   // 定义一个变量a
	a = sin(a);
	printf("%lf\n", a);  // 打印
	return 0;
}

名字空间(关键字:namespace)

为防止名字冲突(出现同名变量),C++引入了名字空间(namespace)
通过 :: 运算符限定某个名字所属的名字空间

#include <cstdio>

namespace first{
	int a;
	void f(){/* ... */}
	int g(){/* ... */}
}

namespace second{
	double a;
	double f(){/* ... */}
	char g;
}

int main(){
	first::a = 2;
	second::a = 6.22;
	first::a = first::g() + second::f();
	second::a = first::g() + 6.333;
	
	return 0;
}

通常有3种方法使用名字空间X的名字name:
1)引入整个名字空间
using namespace X;
2)使用单个名字
using X::name;
3)程序中使用名字时带上空间名
X::name

输入输出

C++的新的输入输出流库(头文件iostream)将输入输出看成一个流,并用输出运算符 << 和输入运算符 >> 对数据(变量和常量)进行输入输出;其中有:
cout 代表标准输出流对象(屏幕窗口)
cin 代表标准输入流对象(键盘);
标准库中的名字都属于标准名字空间 std;

#include <iostream>
#include <cmath>
using std::cout;// 使用单个名字

int main(){
	double a;
	//cout << "从键盘输入一个数"; 这行代码的返回值依然是 cout,所以才可以有下面的代码形式
	cout << "从键盘输入一个数" << std::endl; //endl表示的是换行符,同时也有强制将缓冲区内容全部输出的功能
	std::cin >> a;//通过“名字限定” std::cin; cin表示键盘的输入流对象,>> 等待键盘输入
	a = sin(a);
	cout << a;//cout代表屏幕窗口的输出流对象
	return 0;
}
#include <iostream> //标准输入输出头文件
#include <cmath>
using namespace std;//引入整个名字空间std中的所有名字;cout&cin都属于名字空间std

int main(){
	double a;
	cout << "从键盘输入一个数" << endl;
	cin >> a;
	a = sin(a);
	cout << a;
	return 0;
}

变量定义

变量“即用即定义”(在需要变量的地方,随时定义),且可用表达式初始化

int main(){
	double a = 12 * 3.21;
	double b = a + 1.112;
	
	cout << "a contains " << a << endl;
	cout << "b contains " << b << endl;

	a = a * 2 + 6;
	double c = a + b * a;//“即用即定义”,且可用表达式初始化
	cout << "c contains " << c << endl;
	return 0;
}

程序块变量作用域

程序块{}内部作用域可定义域外部作用域同名的变量,同时在该块内就隐藏了外部变量

#include <iostream>
using namespace std;

int main(){
	double a;
	cout << "Type a number : ";
	cin >> a;
	{
		int a = 1;//"int a"隐藏了外部作用域的“double a”
		a = a * 10 + 4;
		cout << "local number" << a << endl;
	}
	cout << "You typed: " << a << endl;//main 作用域的 “double a”
	return 0;
}

for循环

for循环语义可以定义局部变量

#include <iostream>
using namespace std;

int main(){
	int i = 0;
	for(int i = 0; i<4; i++){
		cout << i << endl;
	}
	cout << "i contains : " << i << endl;
	for(int i = 0; i<4; i++){
		for(int i = 0; i<5; i++){
			cout << "for for i : " << i << endl;
		}
		cout << "for i : " << i << endl;
	}
	return 0;
}

访问全局变量与局部变量

访问和内部作用域变量同名的全局变量,要用全局作用域限定 ::

#include <iostream>

double a = 1.22;

int main(){
	double a = 256;

	cout << "local a : " << a << endl;
	cout << "global a : " << ::a << endl;// :: 是全局作用域限定
	return 0;
}

引用类型

C++引入了“引用类型”,即一个变量是另一个变量的别名;(在C语言中所有的类型都是“值类型”,C语言中定义的每一个变量都是一个内存块的名字);引用变量并不占用单独的内存块,或者说引用变量与原变量都对应着同一个内存块,引用变量和原变量只是同一个内存块的不同名字

#include <iostream>
using namespace std;

int main(){
	double a = 2.22;
	double &b = a;// b是a的别名,b就是a,它俩只是同一个内存的不同的名字
	b = 89;//也就是 a = 89;这俩变量的任意之一的改变都会改变另一个,因为它俩指向的内存块是同一个;
	cout << "a contains : " << a << endl;// Displays : 89
	return 0;
}

引用经常用作函数的形参,表示形参和实参是同一个对象,在函数内部对形参的修改就是对实参的修改。

#include <iostream>

using namespace std;

void swap(int x, int y){
	cout << "swap函数交换前" << x << " " << y << endl;
	int t = x; x = y; y = t;
	cout << "swap函数交换后" << x << " " << y << endl;
}

/*
	x,y代表的是2个int类型的指针变量,函数代码含义:修改的是 x y 指向的内存中的数据,并没有修改 x y 本身 
*/
void swap1(int *x, int *y){
	cout << "swap1交换前:" << *x << " : " << *y << endl;
	int t = *x;
	*x = *y;
	*y = t;
	cout << "swap1交换后:" << *x << " : " << *y << endl;
}

/*
	x y 是实参的引用,对x/y的操作会修改到实参
*/
void swap2(int &x, int &y){
	cout << "swap2数据交换前:" << x << " : " << y << endl;
	int t = x;
	x = y;
	y = x;
	cout << "swap2数据交换后" << x << " : " << y << endl;
}

int main(){
	int a = 3, b = 4;
	swap(a, b);//该函数无法有效变换a,b的值
	cout << "a : " << a << " b : " << b << endl; // Displays 3 4

	int c = 1, d = 10;
	swap1(&c, &d);// 在swap1 中 *x *y 代表的就是 c 和 d,可以得到预期的结果:成功交换
	cout << "c:" << c << " d : " << d << endl;

	int e = 2, f = 20;
	swap2(e, f);//swap2中 x/y 是 e/f 的引用,直接可以划上等号,互相影响
	return 0;
}

当实参占内存大时,用引用代替值(需要复刻)可提高效率;
如果不希望实参被修改,可以用const修饰符;

#include <iostream>
using namespace std;

void change(double &x, const double &y, double z){
	x = 100;
	y = 101; // !!! 此处y不可修改,因为是const double &, 编译时期就会报错
	z = 102;
}

int main(){
	double a,b,c;
	
	change(a, b, c);
	cout << a << b << c << endl;// display: 
	return 0;
}

内联函数

对于不包含循环的简单函数,建议用inline关键字声明为"inline内联函数"; 编译器会将内联函数调用处用其代码展开(替换),称为“内联展开”,避免函数调用开销,提高程序执行效率

#include <iostream>

using namespace std;

inline double distance(double a, double b){
	return sqrt(a*a + b*b);
}

int main(){
	double c = 6, d = 7;
	//下面两行将产生同样的代码
	cout << distance(c, d) << endl;
	cout << sqrt(c*c + d*d) << endl;

	return 0;
}

try-catch

通过 try-catch 处理异常情况,正常代码放在try块,catch中捕获try块抛出的异常

#include <iostream>
#include <cmath>

using namespace std;

int main(){
	int a, b;
	cin >> a;
	cout << a << endl;

	//用法一
	try{
		if(a > 100) throw 100;
		if(a < 10) throw 10;
		throw a/3;
	}catch(int result){
		cout << "Result is: " << result << endl;
		b = result + 1;
	}catch(char *s){
		...
	}
	cout << "b : " << b << endl;

	//用法二
	char zero[] = "zero";
	char pair[] = "pair";
	char notprime[] = "notprime";
	char prime[] = "prime";

	try{
		if(a == 0) throw zero;
		if((a/2) * 2 == a) throw pair;
		for(int i = 3; i <= sqar(a); i++){
			if((a/i) * i == a) throw notprime;
		}
		throw prime;
	}catch(char *conclusion){
		cout << "异常结果:" << conclusion << endl;
	}catch(...){
		cout << "其他所有异常都在此捕获" << endl;
	}
	return 0;
}

默认形参

函数的形参可带有默认值,所有自带默认值的形参必须一律在参数列表对的最右边,这些参数之间不能掺杂无默认形参的形参

#include <iostream>

using namespace std;

int main(){
	
	return 0;
}

函数重载

C++允许函数同名(C语言不允许),只要他们的形参不一样(个数或对应参数类型)
调用函数时,将根据实参和形参的匹配选择最佳函数;
如果有多个难以区分的最佳函数,则一起报错;
注意:不能根据返回类型区分同名函数

#include <iostream>

using namespace std;

double test(double a, double b = 7){
	return a - b;
}
//参数个数不同,正确✅
double test(double a, double b = 7, int c = 5){
	return a - b;
}

int add(int a, int b){
	return a + b;
}
//参数类型不同,正确✅
double add(double a, double b){
	return a + b;
}
//错误❌,编译器无法区分 int add(int a, int b)、void add(int a, int b)
void add(int a, int b){
	a + b;
}

int main(){
	...
}

运算符重载

关键字:operator

#include <iostream>

using namespace std;

struct Vector2{
	double x;
	double y;
}

Vector2 operator * (double a, Vector2 b){
	Vector2 r;
	r.x = a * b.x;
	r.y = a * b.y;
	return r;
}

Vector2 operator + (Vector2 a, Vector2 b){
	Vector2 r;
	r.x = a.x + b.x;
	r.y = a.y + b.y;
	return r;
}

int main(){
	Vector2 k,m;//C++定义的struct类型前不需要再加关键字“struct”
	k.x = 2;//用成员访问运算符 . 访问成员
	k.y = -1;
	m = 3.14 * k;
	cout << "(" << m.x << ", " << m.y << endl;

	Vector2 n = m + k;
	cout << "(" << n.x << ", " << n.y << endl;
	
	return 0;
}
#include <iostream>

using namespace std;

/*
重写ostream对象中的输出运算符 <<
此处并不存在递归的问题,因为函数重载只会重载参数列表是(ostream& o, Vector2 a)的函数
*/
ostream& operator << (ostream& o, Vector2 a){
	o << "(" << a.x << ", " << a.y << ")" << endl;
	return o;
}

struct Vector2{
	double x;
	double y;
}

int main(){
	Vector2 k;//C++定义的struct类型前不需要再加关键字“struct”
	k.x = 2;//用成员访问运算符 . 访问成员
	k.y = -1;
	cout << k << endl;//编译器会将这句编译成:operator <<(cout, k)
	
	return 0;
}

模板template函数

可以理解为java中的泛型

//当前程序求两个int、double变量中的最小值
//普通写法
#include <iostream>
using namespace std;

int minValue(int a, int b){
	return a < b ? a : b;
}

double minValue(double a, double b){
	return a < b ? a : b;
}

int main(){
	int i = 3, j = 4;
	cout << minValue(i, j) << endl;

	double x = 3.14, y = 4.15;
	cout << minValue(x, y) << endl;
	return 0;
}

template 写法

//当前程序求任何可以比较大小的类型变量中的最小值
//使用该模型让编译器自动生成一个针对该数据类型的具体函数
#include <iostream>
using namespace std;

//类型相同
template<class T>
T minValue(T t1, T t2){
	return t1 < t2 ? t1 : t2;
}

//类型不同
template<class T1, class T2>
T1 minValue(T1 t1, T2 t2){
	return t1 < t2 ? t1 : ((T1)t2);
}

int main(){
	int i = 3, j = 4;
	cout << "min of int : " << minValue(i, j) << endl;
	double x = 3.14, y = 10;
	cout << "min of double: " << minValue(x, y) << endl;

	cout << "min of int & double : " << minValue(i, y) << endl;
	return 0;
}

动态内存分配

关键字:new 和 delete 比C语言的malloc/alloc/realloc和free更好, 可以对类对象调用初始化构造函数或析构函数

#define _CRT_SECURE_NO_WARNINGS //windows
#include <iostream>
#include <cstring>
using namespace std;

int main(){
	double d = 3.14;//变量d代表的是一块存放double值的内存块
	/*
		1,指针变量dp:保存的是double类型变量的地址
		2,dp的类型是:double *
		3,dp存放的是 double * 类型值的内存块
	*/
	double *dp;
	/*
		1,取地址运算符&:用于获得变量地址
		2,将double变量d的地址(指针)保存到类型是double *的变量dp中
		3,dp 和 &d 的类型都是:double *
	*/
	dp = &d;
	/*
		1,解引用运算符*用于获得指针变量指向的那个变量(C++中也称为对象)
		2,*dp表示的就是: 以dp变量中的内容为地址的变量,即d
	*/
	*dp = 3.14;     
	cout << "*dp = " << *dp << ", d = " << d << endl;
	
	cout << "Type a Number: " << endl;
	cin >> *dp; //输入一个数字填入 *dp
	cout << "*dp = " << *dp << ", d = " << d << endl;

	/*
		1,new 分配正好容纳double值的内存块(4或8个字节)
		2,并返回这个内存块的地址,而且地址的类型是 double *
		3,这个值保存在dp中,dp指向这个新内存块,不再指向原来的d的地址
		4,但目前这个内存块中的值是未知的
		5,注意:
			5.1,new方法分配的内存块是堆存储空间的,即所有程序共同拥有的自由内存空间
			5.2,d、dp等变量占用的内存是这个程序自身的静态存储空间
			5.3,new会对这个double元素调用double类型的构造函数做初始化
	*/
	dp = new double;
	//以dp存放内容为地址的内存块的值被赋值为 5.14
	*dp = 5.14;
	
	cout << "Type a Number: " << endl;
	cin >> *dp;//输入一个数字填入 *dp
	cout << "*dp = " << *dp << endl;
	
	*dp = *dp + 5;//修改dp指向的内存块的值:5.14 + 5
	cout << "*dp = " << *dp << endl;

	delete dp;//delete 释放dp指向的动态分配的double内存块

	/*
		1,new 使用常数值5,分配了可以存放5个double值的连续内存块
		2,返回值是这个块5个连续内存块的首地址(起始地址),返回值类型是指针类型 double *, 也就是第一个double元素的地址
		3,new会对每个double元素调用double类型的构造函数
	*/
	dp = new double[5]; 
	dp[0] = 1.12;//等价于 *(dp+0) 即 *dp,也即第一个double元素
	dp[1] = dp[0] + 1.13;//等价于 *(dp+1),也即第二个double元素
	cout << "dp[0] = " << dp[0] << ", dp[1] = " << dp[1] << endl;

	/*
		1,如果不加[],只能释放第一个double元素的内存块
		2,加[]释放dp指向的多个double元素占据的内存块
		3,会对每个double元素调用double类型的析构函数以释放资源
	*/
	delete[] dp;

	int n = 8;
	/*
		1,new 可以分配随机大小的double元素
		2,而静态数组则必须是编译期就固定大小,即大小为常量:double arr[20]
	*/
	dp = new double[n];

	//通过下标访问每个元素,并赋值
	for(int i=0; i<n; i++){
		dp[i] = i;
	}

	//打印每一个元素
	//方法一:
	double *p = dp;
	for(int i=0; i<n; i++){
		cout << *(p+i) << endl;//等同于 p[i]或者dp[i]
	}
	//方法二:
	for(double *p = dp, *q = dp + n; p < q; p++){
		cout << *p << endl;
	}
	delete[] dp;
	
	char *s;
	s = new char[100];
	strcpy(s, "hello");//将字符串常量拷贝到s指向的字符数组内存块中
	cout << s << endl;
	delete[] s;//用完以后,需要释放内存块,否则会"内存泄漏"
	return 0;
}

可以理解为:在C的struct类型上,增加了"成员函数";
C的struct可将一个概念或实体的所有属性组合在一起,描述同一类对象的属性;
C++使得struct不但包含数据,还包含函数-用于访问或修改类变量(也可称为对象)的属性

#include <iostream>
using namespace std;

struct Date{
	int d, m, y;
	void init(int dd, int mm, int yy){
		d = dd;
		m = mm;
		y = yy;
	}
	void print(){
		cout << "d = " << d ", m = " << m << ", y = " << y << endl;
	}
}

int main(){
	Date day;
	/*
		通过类对象day调用类Date的方法
	*/
	day.print();
	day.init(4, 6, 1991);
	day.print();
	return 0;
}

自引用

#include <iostream>
using namespace std;

struct Date{
	int d, m, y;
	void print(){
		cout << "y" << y << "-" << m << "-" << d << endl;
	}

	void init(int dd, int mm, int yy){
		d = dd;
		m = mm;
		y = yy;
	}

	Date& add(int dd){
		d += dd;
		/*
			1,this本身代表的是指向调用这个函数的类型对象的指针:Date *
			2,*this代表的就是以调用这个函数的类型对象指针的内容为地址的变量,即调用函数对象本身
			3,这个函数的返回值是 Date& 类型的“自引用”,即调用函数的对象本身
			4,通过返回"自引用",可以连续调用这个函数: day.add(1).add(2);
		*/
		return *this;
	}
}

int main(){
	Date day;
	day.print();
	day.init(4, 6, 1999);
	day.print();
	day.add(1).add(3);
	day.print();
}

成员函数重载运算符

#include <iostream>
using namespace std;

struct Date{
	int d, m, y;
	Date& operator+=(int dd){
		d = d + dd;
		return *this;
	}
	void print(){
		cout << "y" << y << "-" << m << "-" << d << endl;
	}
	void init(int dd, int mm, int yy){
		d = dd;
		m = mm;
		y = yy;
	}
}

int main(){
	Date day;
	day.print();
	day.init(4,6,1999);
	day.print();
	day += 3;
	(day += 3) += 7;
	day.print();
	return 0;
}

构造函数

#include <iostream>
using namespace std;
//每个对象只会调用对应构造函数,不会多次调用不同的构造函数
struct Date{
	int d,m,y;
	//默认构造函数:如果没有定义构造函数,那么编译器就会生成无参数的默认构造函数
	Date(){}

	//自定义构造函数:在有了自定义构造函数后,编译器不会再生成默认的构造函数
	Date(int dd, int mm, int yy){
		d = dd; m = mm; y = yy;
	}

	Date(int dd){
		d = dd;
	}

	Date(int dd, int mm){
		d = dd;
		m = mm;
	}

	void init(int dd, int mm, int yy){
		d = dd; m = mm; y = yy;
	}

	void print(){
		cout << y << "-" << m << "-" << d << endl;
	}
}

int main(){
	Date day;//调用默认构造函数
	Date day2(2, 6, 2003);//调用自定义构造函数
	/*
		在以上的程序中,day3, day4 会找到参数对应的构造函数,找不到就会报错
	*/
	Date day3(3);
	Date day4(4, 6);
	
	day.print();
	day2.print();
}

针对上述程序中的 day, day2, day3, day4需要4个构造函数的情况,也可以只定义一个构造函数:
使用参数默认值

/*
	Date day  对应的调用其实就是:Date day(1, 2, 2002)
	Date day2 对应的调用就是:Date day2(2, 6, 2003)
	Date day3 对应的调用就是:Date day3(3, 2, 2002)
	Date day4 对应的调用就是:Date day3(4, 6, 2002)
*/
Date(int dd = 1, int mm = 2, int yy = 2002){
	...
}

析构函数

析构函数名是~和类名,且不带参数,没有返回值

#include <iostream>
#include <cstring>
using namespace std;

struct Student{
	char *name;
	int age;

	Student(char *n = "no_name", int a = 15){
		/*
			个人理解:此处之所以不直接使用指针变量互相赋值 name = n, 是因为n作为函数
			的局部变量,函数退出后,n也会被回收,name指向的地址不安全
		*/
		int namelen = strlen(n);
		/*
			需要注意的是name的内存是由new分配的,需要通过delete释放,负责会发生内存泄漏
		*/
		name = new char[namelen + 1];
		strcpy(name, n);
		age = a;
	}
}

void f(){
	Student stu;
	Student stu1("abc");
	Student stu2("ace", 20);
	cout << stu.name << "\t" << stu.age << endl;
	cout << stu1.name << "\t" << stu1.age << endl;
	cout << stu2.name << "\t" << stu2.age << endl;
}

//析构函数
virtual ~student(){
	delete[] name;//防止内存泄漏
}

int main(){
	f();
	return 0;
}

class

C++中的 struct 和 class 基本是通用的,唯有几个细节不同:
1, 使用 class 时,类中的成员(变量&函数)默认都是 private 属性的, 在class外部无法直接访问;而使用 struct 时,结构体中的成员默认都是 public 属性的。
2, class 继承默认是 private 继承,而 struct 继承默认是 public 继承
3, class 可以使用模板,而 struct 不能

#include <iostream>
#include <cstring>
using namespace std;

class Student{
private:
	char * name;
	int age;
public:
	Student(char *new_name = "no_name", int new_age = 15){
		int len = strlen(new_name);
		name = new char[len + 1];
		strcpy(name, new_name);
        age = new_age;
		/*
			细节:此处如果需要打印全部name的内容,直接传入char *类型name,cout 内部会通过该指针遍历到 \0;
			如果此处传入的是 *name, 那么传入的类型就是 char 类型,cout会直接打印 name 指向地址的首位字符
		*/
		cout << "Constructor : name = " << name << ", age = " << age << endl;
	}
	char* get_name(){ return name; }
	int get_age(){ return age; }
	void set_name(char *new_name = "no_name"){ 
		/*
			细节:在重新设置name之前,先释放之前的分配的内存,防止内存泄漏
		*/
		delete[] name;
		int len = strlen(new_name);
		name = new char[len + 1];
		strcpy(name, new_name);
	}
	void set_int(int new_age){ age = new_age; }
	virtual ~Student(){
		delete[] name;
		cout << "distructor" << endl;
	}
};

int main(int argc, char *argv[]){
	Student stu;
	Student stu1("stu1");
	Student stu2("stu2", 20);
	cout << "name = " << stu.get_name() << "\t age = " << stu.get_age() << endl;
	cout << "name = " << stu1.get_name() << "\t age = " << stu1.get_age() << endl;
	cout << "name = " << stu2.get_name() << "\t age = " << stu2.get_age() << endl;
	return 0;
}

拷贝构造函数 & 赋值运算符

拷贝构造函数:定义一个类对象时,初始化参数是另一个同类型的对象参数
赋值运算符:同类型前提下,一个对象直接赋值给另外一个对象

1,未自定义的情况下,编译器会自动生成默认的拷贝构造函数&赋值运算符
2,默认拷贝构造函数 & 默认赋值运算符使用的拷贝都是浅拷贝
3,当类中数据成员有指针变量或动态内存,默认的拷贝构造函数和默认的赋值运算符就不适用
3.1,拷贝分浅拷贝和深拷贝,3中的情况,在浅拷贝的模式下,会使得多个对象的指针变量指向同一个内存空间,当释放对象指针对应的内存时,可能会存在对同一个内存多次释放的异常情况

//以下所有代码是:错误代码示范

#include <iostream>
#include <cstring>

using namespace std;

class Student{
public:
	char *name;
	int age;
	Student(char *new_name = "no_name", int new_age = 15){
		name = new char[100];
		strcpy(name, new_name);
		age = new_age;
		cout << "Constructor 申请了100个字节的动态内存空间" << endl;
	}

	virtual ~Student(){
		delete[] name;
		cout << "distructor 释放了100个字节的动态内存空间" << endl;
	}
};

void fun1(){
	cout << "fun1 start" << endl;
	Student s;
	Student m(s);//拷贝构造函数
	cout << "s.name = " << s.name << ", s.age = " << s.age << endl;
	cout << "m.name = " << m.name << ", m.age = " << m.age << endl;
	cout << "fun1 end" << endl;
}

void fun2(){
	cout << "fun2 start" << endl;
	Student s;
	Student m("m_name", 99);
	cout << "s.name = " << s.name << ", s.age = " << s.age << endl;
	cout << "m.name = " << m.name << ", m.age = " << m.age << endl;
	cout << "fun2 end" << endl;
}

int main(int argc, char *argv[]){
	/*
		1,不管是 fun1 fun2 函数,都会报错,原因是同一个快内存被释放多次
		2,解决方案:自定义拷贝构造函数或者赋值运算符
	*/
	fun1();
	fun2();
	return 0;
}
//以下正确代码示范

#include <iostream>
#include <cstring>

using namespace std;

class Student{
public:
	char *name;
	int age;
	Student(char *new_name = "no_name", int new_age = 15){
		name = new char[100];
		strcpy(name, new_name);
		age = new_age;
		cout << "Constructor 申请了100个字节的动态内存空间" << endl;
	}
	
	//自定义拷贝构造函数,对指针进行深拷贝
	Student(const Student &s){
		name = new char[100];
		strcpy(name, s.name);
		age = s.age;
		cout << "自定义拷贝构造函数" << endl;
	}
	//自定义赋值运算符,对指针进行深拷贝
	Student& operator = (Student &a){
		name = new char[100];
		strcpy(name, a.name);
		age = a.age;
		cout << "赋值运算符" << endl;
		return *this;
	}

	virtual ~Student(){
		delete[] name;
		cout << "distructor 释放了100个字节的动态内存空间" << endl;
	}
};

void fun1(){
	cout << "fun1 start" << endl;
	Student s;
	Student m(s);//拷贝构造函数
	cout << "s.name = " << s.name << ", s.age = " << s.age << endl;
	cout << "m.name = " << m.name << ", m.age = " << m.age << endl;
	cout << "fun1 end" << endl;
}

void fun2(){
	cout << "fun2 start" << endl;
	Student s;
	Student m("m_name", 99);
	cout << "s.name = " << s.name << ", s.age = " << s.age << endl;
	cout << "m.name = " << m.name << ", m.age = " << m.age << endl;
	cout << "fun2 end" << endl;
}

int main(int argc, char *argv[]){
	/*
		1,不管是 fun1 fun2 函数,都会报错,原因是同一个快内存被释放多次
		2,解决方案:自定义拷贝构造函数或者赋值运算符
	*/
	fun1();
	fun2();
	return 0;
}

类体外成员函数

1, 必须在类中定义函数声明
2, 类体外定义时要有类作用域(否则就会成文全局外部函数了)
3, 所有成员函数,包括构造函数和析构函数都可以定义在类体外

#include <iostream>
using namespace std;

class Date{
	int d, m, y;
public:
	void print();
	Date(int dd = 1, int mm = 12, int yy = 1991);
	virtual ~Date();
};

Date::Date(int dd, int mm, int yy){
    d = dd;
    m = mm;
    y = yy;
    cout << "constructor" << endl;
}

Date::~Date(){
    //不需要做任何释放工作,因为构造函数没有申请资源
	cout << "析构函数" << endl;
}

void Date::print(){
	cout << y << "-" << m << "-" << d << endl;
}

int main(void){
	Date day;
	day.print();
	return 0;
}

类模板

自定义动态长度数组:

普通写法:

#include <iostream>
#include <cstdlib>
using namespace std;

class Array{
private:
	int size;
	double* data;
public:
	Array(int n){
		size = n;
		data = new double[size];
		cout << "constructor: size = " << size << endl;
	}

    /*
        此处细节:返回值需要是引用;
                如果返回的是double,实际上这是一个常量值,值本身是没有办法继续被赋值的;
                引用是这个值的变量别名,可以被赋值
    */
	double& operator[](int index){
		if(index < 0 || index >= size){
			cerr << "Out of bounds" << endl;
			exit(EXIT_FAILURE);
		}else
			return data[index];
	}

	virtual ~Array(){
		delete[] data;
		cout << "distructor" << endl;
	}
};

int main(void){
	int n;
	cin >> n;
	Array a(n);
	a[0] = 45;
	a[4] = a[0] + 7;

	cout << a[4] << endl;
	return 0;
}

模板写法:

#include <iostream>
#include <cstdlib>
using namespace std;

template <class T>
class Array{
private:
	int size;
	T* data;
public:
	Array(int n){
		size = n;
		data = new T[size];
		cout << "Constructor size = " << size << endl;
	}

	T& operator[](int index){
		if(index < 0 || index >= size){
			cerr << "Out of bounds" << endl;
			exit(EXIT_FAILURE);
		}else
			return data[index];
	}

	virtual ~Array(){
		delete[] data;
		cout << "distructor" << endl;
	}
};

int main(void){
	Array<int> t(5);
	t[0] = 10;
	t[4] = t[0] + 4;
	cout << "t[4] = " << t[4] << endl;

	// t[10] = 20;//t[10]可以模拟异常情况

    Array<string> s(5);
    s[0] = "hello";
    s[1] = "world";
    for(int i=0; i<5; i++){
        cout << s[i] << endl;
    }
	return 0;
}

类型别名(typedef)

在c语言中 #define 与 typedef 有相似的功能,但是也有区别:请在 C语言学习笔记中搜索"typedef 和 #define 的区别"

#include <iostream>
using namespace std;
typedef int INT;

int main(void){
	INT i = 3;//等价于 int i = 3
	cout << i << endl;
	return 0;
}

string

初始化代码一览:

#include <iostream>
#include <string> //typedef basic_string<char, char_traits<char>, allocator<char> > string; 
using namespace std;
typedef string String;

int main(void){
	string s1;//默认构造函数:无参数或参数有默认值
	String s2("hello");//普通构造函数
	s1 = "xxxx";//赋值运算符
	String s3(s1);//拷贝构造函数,等价于 String s3 = s1

	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;

	//表示从第一个字符串参数里取前10个字符
	String s4("this is C_string", 20);
	cout << "s4 = " << s4 << endl;
	
	/*
		从s4的第6个位置后开始,取4个字符赋值给s5
		以:"this is C_string" 为例, 会忽略前6个字符 "this i", s5的最终取值是 s C_
	*/
	String s5(s4, 6, 4);
	cout << "s5 = " << s5 << endl;

	//s6 其实是15个 * 组成
	String s6(15, '*');
	cout << "s6 = " << s6 << endl;

   /*
       通过两个迭代器作为首尾的位置,确定字符串内容
       s4.begin -> start iterator
       s4.end -> end iterator
   */
   String s7(s4.begin(), s4.end() -5);
   cout << "s7 = " << s7 << endl;

   String s8 = "s8_sss";
   cout << "s8 = " << s8 << endl;

   // s1 + "_s9_" + s2 的结果是string类型的对象(变量)
   String s9 = s1 + "_s9_" + s2;
   cout << "s9 = " << s9 << endl;
	return 0;
} 

访问string元素:

#include <iostream>
#include <string>
using namespace std;

int main(void){
	string s1 = "hello";
	string s2 = "_world!";
	s1 = s1 + s2;// s1 += s2
	
	//方法一
	for(int i = 0; i != s1.size(); i++){
		cout << s1[i] << " : ";
	}
	cout << endl;

	//方法二
	/*
		迭代器理解为字符串字符位置;该迭代器是string内部定义的迭代器
		此处的迭代器是常量迭代器,无法通过该迭代器修改字符
		string内部除了const_iterator常量迭代器,还定义了普通迭代器iterator,可以通过iterator来修改字符
	*/
	string::const_iterator it;
	for(it = s1.begin(); it != s1.end(); it++){
		cout << *it << " ~ ";
	}
	cout << endl;

	string::iterator it2;
	for(it2 = s1.begin(); it2 != s1.end(); it2++){
		*it2 = 'AB'; 
		cout << *it2 << " ~ ";
	}
	cout << endl;
	return 0;
}

vector

向量Vector是一个封装了动态大小数组的顺序容器。可以简单的认为,向量是一个能够存放任意类型的动态数组。

#include <vector>
#include <iostream>
using namespace std;

int main(void){
	int size = 10;
	vector<int> students_age(size);
	for(int i = 0; i < size; i++){
		students_age[i] = i;
	}

	for(vector<int>::iterator it = students_age.begin(); it != students_age.end(); it++){
		cout << *it << " ~ ";
	}

	cout << endl;
	return 0;
}

继承

一个派生类从1个或多个父类/基类继承,继承父类的属性和函数,也有其自身的特有的属性和函数。

#include <iostream>
#include <cstring>
using namespace std;

class Employee{
private:
	string name;
public:
	Employee(string n);
	void print();
};

class Manager : public Employee{
private:
	int level;
public:
	Manager(string n, int l = 1);	
	void print();
};

Employee::Employee(string n):name(n)//初始化成员列表
{
}

void Employee::print(){
	cout << name << endl;
}

/*
	正确写法:派生类的构造函数只能描述它自己的成员和其直接基类的初始式,不能去初始化基类的成员
*/
Manager::Manager(string n, int l):Employee(n), level(2){
}

/*
	错误写法:此处name属性是基类的私有属性,不能直接访问
*/
// Manager::Manager(string n, int l):name(n):level(2){
// }

void Manager::print(){
	cout << level << endl;
}

int main(){
	Employee e("e-li");
	Manager m("zhang", 2);
	e.print();
	m.print();

	Employee* employees[100];
	int e_index = 0;
	employees[e_index] = &e;
	e_index++;
	//派生类的指针可以自动转化成基类的指针
	employees[e_index] = &m;
	return 0;
}

多重继承

单个基类派生出多个派生类

class Animal{};
class Cat:Animal();
class Dog:Animal();

单个派生类继承多个基类

class One{};
class Two{};
class Three : One, Two
{};

虚函数

关键字:virtual
虚函数实现了函数多态:基类指针指向派生类对象,调用虚函数时,实际调用的是派生类中的函数实现;
派生类的指针可以自动转为基类指针,

//非虚函数
#include <iostream>
#include <cstring>
using namespace std;

class Employee {
private:
	string name;
public:
	void print();
	Employee(string n);
};

class Manager:Employee{
private:
	int lev;
public:
	void print();
	Manager(string n, int l);
};

Employee::Employee(string n){
	name = n;
}

void Employee::print(){
	cout << "Employee name = " << name << endl;
}

Manager::Manager(string n, int l):Employee(n), lev(l){
}

void Manager::print(){
	cout << "Manager lev = " << lev << "  ;";
	Employee::print();
}

int main(){
	Employee emp("emp");
	Manager man("man", 3);

	Employee* emps[10]; int num = 1;
	emps[0] = &emp; num++;
	emps[1] = &man; num++;
	emps[0]->print();
	emps[1]->print();
	return 0;
}

/*
运行结果:
Employee name = emp
Employee name = man

非虚函数的调用下,即便基类指针指向了派生类对象,最终调用的也是基类的方法
*/
//虚函数
#include <iostream>
#include <cstring>
using namespace std;

class Employee{


};

class Manager{

};

int main(){
	return 0;
}

纯虚函数和抽象类

函数体=0的虚函数即“纯虚函数”;拥有 纯虚函数的类即“抽象类”。

#include <iostream>
#include <cstring>
using namespace std;

class Animal{
protected:
	string m_name;
public:
	Animal(string name){ m_name = name; }
	string getName() {return m_name;}
	virtual const char* speak()=0;
};

//从抽象类派生的类型如果没有实现抽象基类所有的纯虚函数,那么派生类仍然是“抽象类”
class Cow: public Animal{
public:
	Cow(string name): Animal(name){}
	//该类如果不实现纯虚函数,依然是抽象类,抽象类无法构建实例对象
};

class Dog: public Animal{
public:
	Dog(string name): Animal(name){}
	virtual const char* speak(){ return "bark"; }
};

int main(){
	// Cow cow("cow"); 错误调用,Cow是抽象类
	Dog dog("dog");
	cout << dog.getName() << dog.speak() << endl; 
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值