《21天学通C++》[美] —读书笔记(上) 潦草版

第1章  绪论

  1.1 C++简史

        C++是一种面向对象的语言,实现了继承、抽象、多态和封装等特性,C++支持类,类中包含成员数据和成员方法,成员方法是操作成员数据的函数。C++是一种中级编程语言,既可以编写应用程序,也可以编写驱动程序。

  1.2 编写C++应用程序

#include <iostream>    //头文件,C++无.h后缀
int main()
{                      //单行注释
	std::cout << "Hello World!" << std::endl;
	return 0;
}
/*
C++文件扩展名为.cpp,编译器在Linux和MAC中常使用g++和clang++,在Windows中常使用MSVC

集成开发环境eclipse(Linux)、xcode(mac)、visualstudio(windows)
使用g++进行编译:g++ -o hello hello.cpp (-std=c++20)  使用C++20特性需加上括号的命令
使用clang++编译:clang++ -o hello hello.cpp (-std=c++20)
*/

  1.3 C++输入输出

using namespace std;//声明命名空间std
cout<<"Hello";    //调用std::cout,std命名空间中的cout
std::cout << "Hello World"<<endl;//输出
std::cin >> variable;            //输入

  1.4 C++20新增功能

        1、引入了三向比较运算符(也称为宇宙飞船运算符)

        2、模板参数验证和一系列新库,进一步标准化了多线程并通过协程提供同步支持

        3、改进了lambda表达式

        4、引入模块避免了包含头文件的缺点

第2章  常量和变量(同C语言)

        1、命名约定:每个单词首字母大写(帕斯卡命名法)如:MyFirstNumber,第一个单词首字母小写(驼峰命名法)如: myFirstNumber

        2、C++增加bool布尔类型,其值为true和false

        3、使用auto自动推断类型:auto num = true; //编译器将通过值进行类型推断

        4、C++新增:关键字constexpr声明的常量表达式

 第3章  数组和字符串(同C语言)

        C++增加string类:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("Hello World!");	//字符串初始化
	cout << s1 << endl;			//字符串输出
	string s2;
	cin >> s2;					//字符串输入
	string s3;
	s3 = s1 + "_" + s2;			//字符串加法
	s3 = s1;					//字符串赋值
	return 0;
}

第4章  表达式与运算符(同C语言)

        C++新增:三向比较运算符<=> 同时比较>、<、==三种操作

第5章  控制程序流程(同C语言)

第6章  函数

  6.1 带默认值的函数参数

int add(int a, int b = 1)//带默认值的参数必须排在后面,否则无法断定实参的值给哪个形参
{
	return a + b;
}
add(1,2)    //调用,第2个参数用实参
add(1)      //调用,第2个参数用形参默认值

  6.2 函数重载

        函数名称和返回值类型相同,但参数不同称为函数重载

int add(char a);
int add(int a, int b);
int add(double a, int b);

  6.3 引用传递参数

void add(int a, int &b)
{
	b = b + a;    //函数执行后修改引用参数
}
int n = 1;
add(1, n);    //n作为引用

  6.4 内联函数

inline void add(int a, int &b);
//该函数将在调用处展开,不进行函数调用

  6.5 自动推断函数类型

auto add(int a, int b)
{
	return a + b;
}
//编译器根据return语句,推断函数返回值类型

第7章  指针和引用

  7.1 动态分配内存

int *q = new int;
int *p = new int[4];
//如果这时有*p++或p++类似的自增自减操作出现,则p的地址被修改,delete将错误!
delete q;
delete[] p;//此处的指针值应是new返回的原值

  7.2 引用

int a = 10;
int &b = a;//b声明为引用

void func(int &a)//函数形参引用,可直接对实参进行修改
{
}

        引用与const :

int a = 10;
const int &b = a;//const引用,不可修改a的值
//int &c = b;	类型不符,将const引用赋给引用
const int &c = b;

         函数参数引用与const

void func(const int &num)//num值不可进行修改
{
}

第8章 类与对象

  9.1 基本形式

//声明human类
class human{	
private:		//私有成员,class默认private
	int age;	//私有数据,只能类的内部访问和友元访问

public:			//公有成员
	string name;
	void set(int age, string name="wang")//成员函数,通过函数赋值private数据
	{							//参数带默认值
		this->age = age;				//this指针指向对象本身
		this->name = name;
	}
	int get()//成员函数,通过函数获取私有数据的值
	{
		return age;
	}
};
human wang;		//实例化对象,创建一个human对象
wang.name = "wang";//访问类成员
wang.set(18);	//通过public函数接口设置类数据成员,第2参数使用默认值
int age = wang.get();//获取私有成员的值

human* p = new human;//new分配内存
p->name = "wang";	//访问类成员
wang.set(18, "li");//通过public函数接口设置类数据成员

  9.2 构造函数

class human{
private:
	int age;
	int wealth;
public:
	string name;
	//接下来对构造函数进行多种函数重载
	human()		//1、默认构造函数,没有任何构造函数时编译器自动生成
	{				//当存在重载的构造函数时编译器不生成默认构造函数

	}

	human(int age)//2、带参构造函数
	{
		this->age = age;	//当类名与形参名冲突,使用this指针可区分
	}
	human(int age, int wealth = 998)//3、带默认值参数的构造函数,全部形参带默认值时等同默认构造,错误!
	{								//		此时当实参为1个时,情况3与2存在冲突
		this->age = age;
		this->wealth = wealth;
	}
	human(int age, int wealth);//4、构造函数可以声明在类中,定义在类外

	human(int Age, int Wealth, string Name="wang") :age(Age), wealth(Wealth), name(Name)
	{							//5、带初始化列表的构造函数,直接进行初始化
								//		此时,参数也可以带默认值
	}
};
human::human(int age, int wealth)//在类外实现函数
{
	this->age = age;
	this->wealth = wealth;
}

  9.3 析构函数

class human{
private:
	int *p;
public:
	human()
	{
		p = new int[10];//new分配内存
	}
	~human()
	{
		delete[] p;
	}
};

  9.4 拷贝构造函数

拷贝构造函数情况:
1、作为函数参数
2、给新对象初始化赋值
3、函数返回值
class human{
private:
	int *p;
public:
	human()
	{
		p = new int[10];//new分配内存
	}
	human(const human& obj)//拷贝构造函数,对象被复制时编译器调用拷贝构造函数
	{
		p = new int[10];//new分配内存,然后将obj对象数据复制下来(深拷贝)
	}
	human& operator= (const human&obj)//=赋值运算重载
	{
		p = new int[10];//new分配内存,然后将obj对象数据复制下来(深拷贝)
        return *this;
	}
	~human()
	{
		delete[] p;
	}
};
human func(human b)	//形参b不执行构造函数,而是直接复制a,执行析构释放同一地址2次,错误!	
{
    static human a;
    return a;    //返回对象,此时发生拷贝,错误!
}

human a,b;
b = func(a);	//调用拷贝构造,复制对象将它作为参数传递给函数
human c = a;	//调用拷贝构造,通过已有对象初始化新建的对象
				//c不执行构造函数,而是直接复制a,执行析构释放同一地址2次,错误!		
c = a;			//调用赋值重载,不是新对象时,复制要 重载=赋值运算符
//加入拷贝构造函数后,复制对象和对象作为函数实参调用时,将调用构造函数进行内存分配

  9.5 static数据成员

static用于数据成员时,该成员将在所有实例对象中共享,不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化

class human{
public:
	static int age;    //静态数据成员,可以是private或public
};
int human::age = 18;    //初始化静态数据成员
int main()
{
    human::age = 100;    //直接使用类静态数据成员
}        

  9.6 static成员函数

        静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)

        普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针

void print(string name)    //普通函数
{
	cout << "print:" << name<<endl;
}
class human{
public:
	static int age;        //静态数据成员
	static void add()
	{
		++age;	            //使用静态数据成员
	}
	static void show()
	{
		add();                //使用静态成员函数
		print("show");        //使用普通函数
		cout << "age = " << age << endl;//使用静态数据成员
	}
};

  9.7 类只有唯一的实例对象

class human{
private:
	human(const human&obj);			//私有拷贝构造函数
	void operator=(const human&obj);//私有赋值运算符
};
class human{
private:
	human(){};                     //默认构造函数
	human(const human&obj);			//私有拷贝构造函数
	const human& operator=(const human&obj);//私有赋值运算符
	string name;                    
public:
	static human& createobj()	//返回静态对象,无法使用拷贝构造,因此使用引用方式
	{
		static human onlyobj;	//静态对象,可以使用默认构造函数
		return onlyobj;
	}
	void setname(string name)
	{
		this->name = name;
	}
	string getname()
	{
		return name;
	}
};
human& one = human::createobj();//引用唯一静态对象
human two;					//1、不可使用默认构造函数
human * three = new human;	//2、不可使用默认构造函数
human four = one;			//3、不可使用拷贝构造函数
one = human::createobj();	//4、不可使用赋值运算符
human& five = one;			//5、可对static唯一的对象,多次引用

  9.8 析构函数为private

class human{
private:
	~human(){}
public:
	static void Destory(human *p)//自定义的静态析构函数
	{
		delete p;
	}
};
human p;				//析构函数为private,无法创建对象
human *p = new human;	//new对象不会自动销毁,可以创建对象
human::Destory(p);      //调用自定义函数

  9.9 构造函数进行类型转换

class human{
private:
	int age;
public:
	explicit human(int num) :age(num)//explicit禁止构造函数进行类型转换
	{
	}
	void show(human h)
	{
		cout << h.age << endl;
	}
};
human h(10);	//正常情况
h.show(h);

human g = 11;	//禁止类型隐式转换
h.show(9);		//禁止类型隐式转换

  9.10 友元

友元函数:
class human{
private:
	int age;
public:
	human(int a=1) :age(a){}
	friend void show(const human& p);//声明友元函数
};
void show(const human& p)			//定义友元函数
{
	cout << p.age << endl;
}
human h(9);
show(h);
友元类:
class human{
private:
	int age;
public:
	human(int a=1) :age(a){}
	friend class student;    //声明友元类
};
class student{
public:
	void show(const human &p)    //可以使用human中的private成员
	{
		cout << p.age << endl;
	}
};
human h(10);
student s;
s.show(h);

  9.11 类使用聚合初始化

        数组、结构体、共用体、类都可进行聚合初始化

        在满足这些条件时,类也可以进行聚合初始化:

        1、不包含用户定义的构造函数

        2、只包含公有和非静态的数据成员,不包含私有或保护的数据成员

        3、不包含任何虚成员函数

        4、不涉及继承或只涉及公有继承(不涉及私有继承、保护继承、虚继承)

class human{
public:
	int age;
	string name;
	double dou;
};
human h = { 25, "wang", 3.1415926 };

第9章 继承

基类publicprotectedprivate
public继承publicprotectedprivate
protected继承protectedprotectedprivate
private继承privateprivateprivate
不论什么继承方式,在派生类中可访问可访问不可访问

9.1 继承

class human{
private:
	int num1;
public:
	int num2;
protected:
	int num3;
};
//public继承
class student :public human{    
	//num1不可访问,num2和num3可访问
};
student s;//num1 num3不可访问,num2可访问

//protected 继承
class student :protected human{
	//num1不可访问,num2和num3可访问
};
student s;//num1 num2 num3均不可访问

//private 继承
class student :private human{
	//num1不可访问,num2和num3可访问
};
student s;//num1 num2 num3均不可访问

  9.2 基类与派生类的构造与析构问题

构造顺序:基类的数据成员基类的方法子类的数据成员子类的方法
虚构顺序:子类的方法子类的数据成员基类的方法基类的数据成员

  9.3 基类初始化

class human{
protected:
	int age;
public:
	human(int a):age(a){	//基类构造函数
	}
};
class student :public human{
public:
	student():human(1){	//派生类的构造函数,并调用基类构造函数初始化基类
	}
};

  9.4 基类与派生类方法名称冲突

class human{
protected:
	int age;
public:
	human(int a) :age(a){}		//构造函数
	void show()
	{
		cout << age << endl;
	}
};
class student :public human{
public:
	string name;
	student(string name, int age=18) :human(age){	//构造函数
		this->name= name;
	}
	void show()				//直接覆盖基类的show函数
	{
		human::show();		//在派生类中调用基类方法
		cout << age << endl << name << endl;
	}
};
student s("wang",19);
s.show();			//默认调用派生类show
s.human::show();	//通过作用域解析运算符::调用基类show

  9.5 多继承

class human{
public:
	int age;
	void show()
	{
		cout << "human:" << age << endl;
	}
};
class teacher{
public:
	int age;
	void show()
	{
		cout << "teacher:" << age << endl;
	}
};
class student :public human,public teacher{		//多继承
public:
	int age;
	void show()
	{
		cout << "student:" << age << endl;
	}
};
student s;
s.age = 100;			//默认调用派生类类
s.show();				

s.teacher::age = 99;	//调用基类
s.teacher::show();

s.human::age = 98;		//调用基类
s.human::show();

  9.6 final禁止继承

class human final{					//使用final关键字禁止继承
public:
};
class student :public human{		//human无法进行继承,错误!
public:
};

第10章 多态

   10.1 虚函数与虚函数表

        虚函数:

class human {					
public:
	void show()
	{
		cout << "human:基类" << endl;
	}
};
class student :public human{		
public:
	void show()
	{
		cout << "student:派生类" << endl;
	}
};
void func(human& h)		//函数形参为基类
{
	h.show();          //传入派生类对象,将调用基类成员函数
}
student s;
func(s);			


//使用virtual虚函数:传入派生类对象,将调用派生类成员函数
class human {					
public:
	virtual void show()        //将基类函数声明为virtual
	{
		cout << "human:基类" << endl;
	}
};

         虚函数表:

class human {					
	int age;
	virtual void show(){}    //虚函数
};
class student {		
	int age;
	void show(){}
};
class teacher{
	void show();
};
sizeof(human)  sizeof(student)  sizeof(teacher)    //8:4:1

编译器为每个有虚函数的类创建一个虚函数表,虚函数表指针指向该虚函数表

虚函数表:是一个数组,元素为函数指针,指向每个虚函数的实现

实参:派生类对象
形参:基类对象
结果:被函数解读为基类对象,但是虚函数表指针,指向派生类的虚函数表,调用派生类成员函数

  10.2 纯虚函数与抽象基类

class human {	
public:
	virtual void show() = 0;	//声明为纯虚函数,于是该类成为抽象基类
};
class student :public human{	
public:
	void show()					//纯虚函数必须实现
	{
		cout << "class student" << endl;
	}
};
void func(human& h)
{
	h.show();
}
student s;
func(s);

  10.3 虚析构函数问题

class human {	
public:
	human()
	{
		cout << "human 构造" << endl;
	}
	virtual ~human()        //声明为虚析构,将同时调用基类与派生类的析构函数
	{
		cout << "huamn 析构" << endl;
	}
};
class student :public human{	
public:
	student()
	{
		cout << "student 构造" << endl;
	}
	~student()
	{
		cout << "student 析构" << endl;
	}
};
void func(human* h)          //形参为基类对象
{
	delete h;                //调用析构函数时,仅调用基类析构
}
student *p = new student;    //声明派生类对象
func(p);

  10.4 虚继承解决“菱形问题”

class human{
public:
	int age;
	human()
	{
	}
};
class man:public human{};
class women :public human{};
class student :public man, public women{};

student s;	//此时通过2个父类,创建2个human对象
s.age = 100;//有2个age,错误!

         使用虚继承:

//将2个父类声明为虚继承
class man:public virtual human{};
class women :public virtual human{};

  10.5 override与final

        override明确覆盖意图:

class human{
public:
	virtual void print()
	{
	}
};
class student :public human
{
public:
	void print() const			//在类成员函数后加const,表示该函数不会修改类的数据成员
	{
	}
};
void func(human &h)
{
	h.print();		//由于派生类加const,基类的虚函数没有覆盖,因此调用基类的函数
}

//加入override
class student :public human
{
public:
	void print() const override		//明确指出覆盖基类虚函数,
	{
	}
};

         final禁止覆盖:

class human{
public:
	virtual void print(){}
};
class student :public human
{
public:
	void print() override final{}		//override明确覆盖基类print函数,final不允许后续派生类再覆盖
};
class teacher :public student{
public:
	void print(){}	//此时无法再进行虚函数覆盖,错误!
};

第11章 运算符重载

  11.1 基本运算符重载

//1、++与--前缀运算符
    int age;
    human& operator++()
    {
    	age++;
    	return *this;
    }
//2、++与--后缀运算符
	int age;
	human operator++(int)
	{
		human old;			//临时对象
		old.age = this->age;//复制数据
		age++;
		return old;			//返回旧对象
	}
//3、cout<<输出运算符
	#include<sstream>
	int age;
	string str;
	operator const char*()
	{
		ostringstream format;
		format << age;
		str = format.str();
		return str.c_str();
	}
//4、加法+与减法-运算符(其它算数运算符类似)
	int age;
	human operator+(int num)
	{
		human temp;				//临时对象
		temp.age = age + num;
		return temp;			
	}
//5、+=与-=运算符(其它复合赋值运算符可类似仿写)
	int age;
	void operator+=(int num)
	{
		age += num;
	}
//6、==与!=运算符(其它关系运算符可类似仿写)
	int age;
	bool operator==(const human &p)const
	{
		return (age == p.age);
	}
//7、=赋值运算符
	int age;
	human& operator=(const human& p)
	{
		//copy操作
		age = p.age;
		return *this;
	}
//8、[]索引运算符
	int arr[10];
	int& operator[](const int index)
	{
		return arr[index];
	}
//9、()函数运算符
	void operator()(string str)const
	{
		cout << str << endl;
	}

  11.2 移动构造与移动赋值

//移动构造函数
	int age;
	int *p;
	human(human&& src)
	{
		if (src.p != NULL)
		{
			age = src.age;
			p = src.p;
			src.p = NULL;//将引用的参数去除,跳过中间复制过程,直接将地址传递
		}
	}
//移动赋值运算符
	human& operator=(human&& src)
	{
		if (src.p != NULL)
		{
			age = src.age;
			p = src.p;
			src.p = NULL;
		}
		return *this;
	}
//对象a+对象b,表达式的值为对象,要返回human
	int age;
	human operator +(const human& append)
	{
		human temp;		//创建临时变量
		temp.age = age + append.age;
		return temp;
	}

human a, b;
human c(a + b);//(移动构造函数)加法,产生中间对象,再通过拷贝构造初始化对象,
               //坏处:深拷贝+中间对象
               //改进:直接将中间对象的指针拿过来,清除中间对象,避免深拷贝
human d;
d = a + b + c; //(移动赋值运算符)加法,产生中间对象,进行赋值运算符,对中间对象进行深拷贝,
			   //做法同上,避免深拷贝操作

  11.3 类型转换符

1、static_cast:(可将指针向上转基类,可将指针向下转派生类)
    //static_cast执行编译阶段检查,不执行运行阶段检查

    class student :public human
	human h,*parent;
	student s,*son;
	
	parent = &s;	//基类指针可以指向派生类对象
	son    = &h;	//派生类指针不可以指向基类对象,错误!
	son    = static_cast<student*>(&h);	//static_cast类型转换
                    //派生类指针指向基类对象时,使用派生类数据成员将错误

2、dynamic_cast:(运行阶段类型识别)
    class human{
    	virtual void print(){}			//基类必须包含多态
    };
    class student :public human{		//继承human
    };
    class teacher :public human{		//继承human
    };
    void func(human* h)
    {
    	student* s = dynamic_cast<student*>(h);//实参student,形参human,再转到student
    	if (s)            //转换失败指针为NULL
    		cout << "student YES" << endl;
    	else 
    		cout << "student NO" << endl;

    	teacher* t = dynamic_cast<teacher*>(h);//实参teacher,形参human,再转到teacher
    	if (t)
    		cout << "teacher YES" << endl;
    	else
    		cout << "teacher NO" << endl;
    }
    student s;
    teacher t;
    func(&s);//结果:student YES  teacher NO
    func(&t);//结果:student NO   teacher YES
    //当func函数内使用static_cast进行类型转换时,结果全为YES,因为基类形参可转为派生类

3、reinterpret_cast:(re interpret表示重新解释,可以强行转换类型)

	int *p;
	string s = "Hello";
	p = reinterpret_cast<int*>(&s);
	cout << *p << endl;

4、const_cast:(关闭对象的const访问修饰符)

    class human{
    public:
    	int age;
    	human(int a = 0) :age(a){}
    };
    void func(const human& p)
    {
    	human& h = const_cast<human&>(p);    //此处强制关闭const,修改对象的值
    	h.age = 100;
    }

    human h;
    func(h);
    cout << h.age << endl;

5、总结
    double pi = 3.1415926;   
//1、C++ style
    int num = static_cast<int>(pi);
//2、C style
    int num = (int)pi;
//3、编译器
    int num = pi;


第12章 模板与宏

  12.1 宏

        1、#define定义常量(同C语言)

        2、#define编写宏函数(同C语言)

  12.2 模板

        1、模板函数:

template <typename T>	//声明该函数为模板,template和typename为关键字,T为替换的名称
const T& getmax(const T& a, const T& b)	//函数
{
	if (a > b)return a;
	else return b;
}
int a = 1, b = 2;
dispaly<int>(a, b);	//带<int>指明类型为int
//编译器生成函数:const int& getmax(const int& ,const int&)

double x = 1.99, y = 2.01;
dispaly(x, y);		//不带时,编译器自动推断,对于模板类必须显式的指明类型
//编译器生成函数:const double& getmax(const double& ,const double&)

        2、模板类:

//1、模板类基本语法
template <typename T1=int, typename T2=int>//带默认参数,同函数类似
class human
{
private:
	T1 value1;
	T2 value2;
public:
	human(const T1& va1, const T2& va2):value1(va1), value2(va2){}
	void print()
	{
		cout << value1 << endl << value2 << endl;
	}
};

human<const char*,double>p1("Hello", 1.99);//带类型值
human<>p2(6, 1.99);			//不带类型值,使用默认值,(但将1.99解释为int)

//2、使用student类作为模板类型参数
class student{
public:
};
student s;
human<int, student>p3(6, s);

//3、对human模板的具体化:(可能在使用不同类型实例化模板时,需要增删不同的成员,因此具体化模板)
template<> class human<int, int>{
private:
	int value1;
	int value2;
	string name;	//增加数据成员
public:
	human(const int& va1, const int& va2) :value1(va1), value2(va2){}
	void print()
	{
		cout << value1 << endl << value2 << endl;
	}
};

//4、模板类静态成员
template <typename T>
class human
{
public:
	static T num;		//静态成员
};
template<typename T> T human<T>::num; //初始化模板类的静态成员
human <int>a;
a.num = 100;	//不同类型具体化的类,静态成员不同
human<double>b;
b.num = 3.1415926;

        3、参数数量可变的模板:

template<typename T1, typename T2>	//原始模板函数
void func(T1& result, T2& value)
{
	result = result + value;
}
									//可变参数模板函数
template <typename T_1, typename T_2, typename...T_N>
void func(T_1& result, T_2 value_1, T_N... value_n)//参数1使用引用作为结果
{
	result = result + value_1;
	return func(result, value_n...);	//反复处理参数
}
double result=0;
func(result, 2, 3, 4.2, 5, 6, 7.123);
string name;
func(name, "Hello "," World!");
cout << result << endl << name.c_str() << endl;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值