C++ primer plus 的全部知识点总结
我将本书的内容总结为以下5个部分
1. c++项目的文件组织和内存模型
2. 类和对象
3. 泛型编程
4. STL
5. C++的异常处理机制和文件输入输出
主要分以下几个部分:
1.c++项目的文件组织和内存模型
1.c++项目采用头文件和源代码文件单独编译链接运行,为了避免多次包含一个文件采用的
#ifndef _
#define_
#endif
或 #pragma once进行
2. 全局变量,局部变量 静态变量,内部链接变量,外部链接变量全局变量:作用域为整个文件的变量,主函数运行完销毁
局部变量:作用域为代码块的变量,存储在栈中,代码块运行完销毁
静态变量:加static关键字的变量,存储在堆中,代码块运行完仍然存在
内部链接变量:加static关键字的全局变量变为具有内部链接性的变量,内部链接性指仅在单cpp文件内部范围内可见
外部链接变量:全局变量默认具有外部链接性,加extern使其显式具有外部链接性,外部链接性是指在其他cpp文件可见该变量,由于变量的单定义规则,定义声明只能一次1.cpp extern int a = 10;/ int a =10; 定义声明只能一次 2.cpp extern int a; 引用声明可多次 3.cpp extern int a; 引用声明可多次 4.cpp static int a; 内部链接变量隐藏外部变量;
3.名称空间 namespace
名称空间是对函数变量的名称修饰
名称空间具有开放性,比如可以向std标准函数库中名称空间添加变量a; namespace std { int a};
名称空间具有可嵌套性
匿名名称空间内的变量相当于内部链接性静态变量
using声明指令和using编译指令,using声明指令可以检测命名冲突而不是直接覆盖
2.类和对象
类
2.1 类的构成:
类的成员 | 可能类别 |
---|---|
私有成员 | 常量和引用声明,静态常量声明和定义,枚举常量声明和定义,变量声明,嵌套类声明,嵌套结构体声明,动态数据结构变量指针声明,静态数据结构变量声明,友元类声明 |
保护成员 | 多重继承的中间模块等 |
公有成员 | 默认构造函数,静态成员函数,返回对象或引用的函数,自动类型转换构造函数,强制类型转换函数,虚函数,纯虚函数,友元函数,using权限重定义,函数重载,运算符重载,析构函数,const成员函数 |
2.1.1 类的私有成员
- 常量和引用声明:常量如const int a;引用如:int& a; 这种跟引用一样,必须在创建时定义且仅一次定义,只能在类的构造函数中采用成员列表初始化而不可在构造函数中初始化。
- 静态变量为所有对象共享,存储类的状态;
- 静态常量和匿名枚举常量声明:单独在堆中的内存,允许在类的声明中定义赋值
const static int a=10; / enum { label =10;}
- 在类声明中嵌套类,结构体,枚举类型,是为了在类作用域内使用这些数据类型
- 枚举类型enum type{ }可以声明枚举类型变量,为避免枚举变量名称冲突,C++11 在type前加class区别
enum class body { eye,hand, foot} enum class animal { eye, wing } body a = body::eye animal n = animal::eye 避免eye的冲突
2.1.2 类的公有成员
成员函数 = 调用对象+函数+输入参数对象+返回对象
- 默认构造函数,默认析构函数,默认复制构造函数,赋值运算符,地址运算符,为自动提供的成员函数
- 加static的成员函数:静态成员函数,不能通过某个对象调用,必须通过类的作用域解析符调用;
- 加const的成员函数:调用该成员函数的对象不可被修改
- 返回对象或者引用的函数
- 返回对象:输入对象经过运算得到临时对象,返回临时对象的拷贝必须返回对象。
- 返回const对象:返回的临时对象拷贝为const对象;
- 返回指向const对象的引用:返回输入参数对象的引用,输入为const对象
- 返回指向非const对象的引用:返回非常量的调用或者参数对象,来实现对调用对象或参数对象的修改, 用return *this;来返回调用对象;
- 单参数的类的构造函数:实现一般类型->类类型:只有单参数的类的构造函数可以充当类的自动 类型转换,加关键字explicit可以关闭这种隐式类型转换,但任然允许显示类型转换;
- 类类型->一般类型的转换函数:operator typename(); 无返回类型有返回值,无参数对象
- 成员函数重载
- 运算符重载:1.成员函数 2.非成员函数(友元函数) ,返回类型 + operator +运算符+参数对象,
非成员函数不可以重载 = , () , [ ] , ->- 友元函数:friend+ 函数+输入对象+返回对象,可重载运算符,可以访问类的私有成员,视为扩展接口
- 函数重写:每个重写的函数都要用虚函数virual,派生类的对应函数必须全部重写,不然变成覆盖,没重写的函数都会被隐藏。
- 虚析构函数:基类要提供虚析构函数,基类指针new派生类时delete只能调用基类的析构函数,必须采用虚析构函数
- 纯虚函数:virtual 函数 =0;使类变为抽象基类,可实现也可不实现,接口类的作用;
- 使用using给与派生类接口使用基类接口的权限重定义;
可以把常见的类的所有成员列出如下:
#pragma once
#ifndef BASE_H_
#define BASE_H_
#include <iostream>
class Base
{
private:
enum class person { student, teacher };//枚举变量
enum { id=1};//匿名枚举常量
const static int id=10;//静态常量
const std::string address_c;//常量,必须通过成员列表初始化
std::string& address;//引用,必须通过成员列表初始化
static int num_object;//静态变量
struct Node {
std::string name;
Node* next;
};//嵌入结构体类型声明,作用域为整个类
int num;//成员变量
protected:
void add();
public:
Base();//类的默认构造函数
explicit Base(int n);//类的构造函数,类的自动类型转换函数,关闭隐式转换
operator int() const;//强制类型转换函数
Base(Base& b);//类的复制构造函数
Base& operator*(Base& b)const;//运算符重载
static void show();//静态成员函数
virtual ~Base();//虚析构函数
//------------------
friend std::ostream& operator<<(std::ostream& os, Base& bs);//友元函数重载运算符
};
#endif
2.2 对象
- 对象数组:用大括号初始化,可以采用不同的类的构造函数;
Base n[]= {Base(),Base(10),Base()};
- 对象的作用域:正常的对象的作用域为代码块,特殊地,如果new一个对象并通过对象指针得到,则除非调用delete清除堆内存否则不会调用类的析构函数;
需要注意:Base* p = new Student(); delete p;
此时基类必须为虚析构函数否则仅调用基类的析构函数析构对象。
- 定位New不能与delete的搭配使用,因此通过定位New创建的对象Student1 和Student2 必须显式调用各自的析构函数而不可以直接delete被定位的基内存。
char* p = new char[100]; Base* c1; Base* c2; c1 = new(p)Base; c2 = new(p + sizeof(Base))Base(); c1->~Base(); //必须显示调用 c2->~Base();//必须显示调用 delete[] p;
2.3 动态内存分配的类
动态内存分配的类
对于可变大小的类的成员通常采用动态内存分配,如可变数组,vector容器,常涉及到STL里的容器作为类的成员,如果是自己写的数据结构如链表,自定义队列,自定义堆栈,通常存储数据结构的地址指针作为成员变量。
当成员变量为指针时通常为new申请的堆内存,默认的复制构造函数,默认的赋值函数执行浅复制,因此需要显示定义复制构造函数,赋值函数和析构函数(delete对应new)
//Base.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
class Base
{
private:
char* s;
int len;
public:
Base();
Base(int a);
Base(Base& bs);
Base& operator=(Base& bs);
virtual ~Base();
};
//Base.cpp
#include "pch.h"
#include "Base.h"
Base::Base()
{
len = 3;
s = new char[len + 1];
strcpy(s, "C++");
}
Base::Base(Base & bs)
{
len = strlen(bs.s);
s = new char[len + 1];
strcpy(s, bs.s);
}
Base & Base::operator=(Base & bs)
{
if (this == &bs)
return *this;
len = strlen(bs.s);
this->s = new char[len + 1];
strcpy(s, bs.s);
return *this;
}
Base::~Base()
{
delete[] s;
std::cout << "your class has been destoryed" << std::endl;
}
//CC.cpp
#include <iostream>
#include "Base.h"
int main()
{
Base n;
Base m;
m = n;
}
2.4 类与类的关系
2.4.1 继承
继承 | 特殊类 |
---|---|
私有继承 | |
保护继承 | 模块类 |
公有继承 | 抽象基类(接口类) |
多重继承 | 虚基类 |
类的继承是派生类对基类的接口和实现的继承,派生类可见基类的保护成员和共有成员的接口却不可见基类的私有成员,构造派生类对象时必须先构造基类对象,由于派生类不可访问基类的私有成员故必须调用基类的构造函数来构造基类的成员对象才可以,使用成员初始化列表方法。
Student::Student(int len, const char s[], std::string n):Base(len,s),name(n){}
重写:利用虚函数重写基类的方法必须全部重写否则没有对应重写的虚函数被覆盖,按照指针或引用的对象调用方法,如果本层没有找到方法会向上寻找最近祖先中的方法;
覆盖:派生类中未被重写的与基类的同名函数,在派生类中会覆盖基类,使派生类对象不可用基类被覆盖的函数
C++的向上和向下强制类型转换–>参考引用文章(https://www.cnblogs.com/xiaodingmu/p/7351919.html)
抽象基类ABC,包含纯虚成员函数(virtual function()=0)的类成为抽象基类ABC,抽象基类用作接口类不可实例化为对象,其派生类必须实现纯虚函数接口;(纯虚函数不需要定义,但C++甚至允许定义纯虚函数)
//human.cpp #pragma once #include <string> #include <iostream> class human { private: std::string name; int age; public: human(); human(std::string name, int age); virtual void eat()=0;//纯虚函数 virtual ~human(); }; //student.cpp class student : public human { public: student(); virtual void eat();//实现接口 ~student(); };
1->2->3 1. human(std::string name, int age); 2.1 student::student(std::string name, int age, int id):human(name,age),s_id(id){} 2.2 teacher::teacher(std::string name, int age, int id):human(name,age),t_id(id){} 3. postdoctor::postdoctor(std::string name, int age, int s_id, int t_id, int post_year):human(name,age),teacher(name, age,t_id),student(name,age,s_id)}{}
调用函数的二义性: 2.1 和 2.2 都有show()函数
1. postdoctor pd; pd.student::show();
2.class postdoctor { public: void show() { student::show() } };
如果继承类成员无new动态内存成员,继承类的默认复制,赋值,析构函数会对基类部分进行显示调用基类的相对应函数,对于has-a 关系的类内部存在类对象也是如此。
!如果继承类成员有new动态内存成员,则必须要显示定义继承类的复制构造,赋值,析构函数对应继承类部分的处理,基类部分则直接调用显示构造,复制函数。
2.4.2 包含
比较继承,包含,使用三者关系的区别:
关系 | 说明 |
---|---|
继承(is-a) | 继承基类的接口和实现 , 派生类具有基类所有的属性和方法 |
包含(has-a) | 类中含有另一个类的对象,表示一个类包含另一个类,只继承实现而不继承接口,表示组成而不表示所属 |
使用(use-a) | 类中含有另一个类的指针指向类的对象,表示一个类使用另一个类, |
//human.h
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
enum sex { male, female };//定义枚举类型
class Human
{
private:
std::string name;
sex s;
public:
Human();
Human(std::string name, sex s);
Human& operator=(Human& h);//显示定义复制函数
operator std::string() const;
void showInfo()const;
virtual void eat() const = 0;
virtual void sleep()const = 0;
virtual ~Human();
//-------------------------------------------------
friend std::ostream& operator<<(std::ostream& os,const Human& h);
};
//worker.h
#pragma once
#include "Human.h"
class worker :
public Human//继承,继承类human
{
private:
int w_id;
public:
void eat() const;//实现了ABC的接口
void sleep() const;//实现了ABC的接口
worker();
worker(std::string name,sex s,int id);
~worker();
//---------------------------
friend std::ostream& operator<<(std::ostream& os, worker& w);
};
//factory.h
#pragma once
#include "worker.h"
class factory
{
private:
std::string postion;
int num_workers;
std::vector<worker> members;//包含,包含类worker的对象
public:
factory();
factory(std::string pos,int num_wks);
bool add_worker(const worker& w);
void rest()const;
~factory();
//-----------------------------------------
friend std::ostream& operator<<(std::ostream& os, const factory& fc);
};
私有继承和包含 |
---|
共同点:都只继承了类的实现而没有继承类的接口,都不可以对类的私有成员访问; |
区别:私有继承:通过公有成员来访问继承而来的私有成员接口包含:通过对象来调用访问成员接口 |
2.4.3 友元
友元函数 | 友元类 |
---|---|
帮外界打开一个不需要对象就可以访问所有成员的接口 | 帮友元类打开一个通过对象访问所有成员的接口 |
友元类:在一个类中任意位置定义友元类,则友元类可以访问此类的私有成员所有成员函数;
//factory.h
#pragma once
#include "worker.h"
class factory
{
friend class college;//定于类college 是factory类的友元类
private:
std::string postion;
int num_workers;
std::vector<worker>* members;
public:
factory();
factory(std::string pos,int num_wks);
bool add_worker(const worker& w);
void rest()const;
~factory();
//-----------------------------------------
friend std::ostream& operator<<(std::ostream& os, const factory& fc);
};
//college.h
#pragma once
#include "factory.h"
class college
{
public:
college();
void comunication(factory& f);
~college();
};
//college.cpp
void college::comunication(factory& f)
{
f.postion = "college";//由于college是factory的友元类,故在college作用域中的factory对象可以访问私有成员。
}
//-export
worker has been destroyed!
human has been destroyed
worker has been destroyed!
human has been destroyed
worker has been destroyed!
human has been destroyed
this factory has 3 members
this factory is in beijing
sex: 0
name: xiaoming
id: 22
sex: 0
name: lihua
id: 18
sex: 0
name: xiaoli
id: 20
this factory has 13 members
this factory is in beijing
sex: 0
name: xiaoming
id: 22
sex: 0
name: lihua
id: 18
sex: 0
name: xiaoli
id: 20
this factory has 13 members
this factory is in college
sex: 0
name: xiaoming
id: 22
sex: 0
name: lihua
id: 18
sex: 0
name: xiaoli
id: 20
factory has been destroyed!
worker has been destroyed!
human has been destroyed
worker has been destroyed!
human has been destroyed
worker has been destroyed!
human has been destroyed
3. 泛型编程
C++的泛型编程
Template<typename/class T,int a,int b,… > void Func{ … }
2.隐式实例化,通过参数来实例模板,如:
template <class T> T add(T n1,T n2) { return n1 + n2; } int main() { using std::cout; using std::endl; int c = add(11, 12); cout << c; }
2.显式实例化:当函数参数不足以实例模板时只能通过显式实例化,去掉返回类型在函数名后加<typename, int …>即为显式实例化,例如change<double,10>
#include <iostream> #include "college.h" template <typename T,int n> T* change() { T a[n]; for (int i = 0; i < n; i++) { a[i] = i; }; return a; } int main() { using std::cout; using std::endl; cout << change<double, 10>()[7]; }
3.具体化 Template<>void [Func]<C,>(C&){ … };
template <typename T1,typename T2,int n> void sum() { std::cout << sizeof(T2)+sizeof(T1) << "模板 " << n; }//模板 // template<typename T1,typename T2> void sum<T1,T2,10>() { }//部分具体化 template<> void sum<int, int, 10>() { std::cout << "显式具体化" << std::endl; }//显式具体化
优先顺序:一般函数 > 实例化 > 具体化 > 模板
模板类:
template<class T(类型参数),int n…(非类型参数)> class ArrayTP{ … }
template <class T, int n> class stack { private: T a[n]; public: void show()const; };//数组模板 template <class T> class stack<T,10> { private: T a[10]; public: void show()const; };//部分具体化 template <> class stack<int, 10> { private: int a[10]; public: void show()const; };//显式具体化 template class stack<int, 11>;//显式实例化 int main() { using std::cout; using std::endl; stack<int, 10> n;//隐式实例化 }
C++泛型编程的应用:
STL:1. Container 2. Algorithm 3. Iterator
4. STL
智能指针:auto_ptr, unique_ptr, shared_ptr
using std::cout;
using std::string;
//--------auto_ptr ----------------
std::auto_ptr<int> p(new int(3));
std::auto_ptr<int> f(p);
//std::cout << *p;//错误,p引用的对象已销毁
//std::auto_ptr p = new int; 错误!!
//---------unique_ptr-----------------------------------
std::unique_ptr<int> q(new int(10));
//std::unique_ptr<int> w(q);错误!unique_ptr对指针的引用比较专一
std::unique_ptr<int> w(std::move(q));//正确
//std::cout << *w;
auto uq = new std::unique_ptr<int>();
//--------------------------------------------------------
//-----------shared_ptr
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);
std::shared_ptr<int> p3(p2);
std::cout << p3.use_count();//=3,共三个引用共享
auto p4 = new std::shared_ptr<int>(p3);
std::cout << p4->use_count();//=4,共四个引用共享
auto p5 = std::make_shared<int>(10);//内存分配+指针
智能指针:auto_ptr, unique_ptr, shared_ptr
Vector容器
#include <iostream>
#include "college.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
int main()
{
using std::cout;
using std::string;
std::vector<int> ratings(5);//定义长度为5的vector容器
std::vector<int> hit(10);
for (int i = 0; i < 5; i++)
{
ratings[i] = i;
//std::cout << ratings[i] << std::endl;
}
for (int i = 0; i < 10; i++)
{
hit[i] = i+rand()%9;
//std::cout << hit[i] << std::endl;
}
std::swap(ratings, hit);//交换vector内容
for (int i = 0; i < 10; i++)
{
std::cout << ratings[i] << std::endl;
}
//一般[i]遍历
//for(auto pd = ratings.begin();pd!=ratings.end();pd++)
//std::cout << *pd << std::endl;
//指针遍历
//std::vector<int>::iterator pd = ratings.begin();//迭代器构造方式1
auto pd = ratings.begin();//迭代器构造方式2
std::vector<int> g(10);
ratings.insert(pd, g.begin(), g.end());
for (auto i : ratings)
std::cout << i;//for循环的智能指针遍历
}
5. C++的异常处理机制和文件输入输出
。。。。。。待后续补充