C++ 基础思维导图(一)
目录
1、C++基础
IO流
namespace
引用、const
inline、函数参数
重载
2、类和对象
类举例
3、 内存管理
new/delete
对象内存分布
内存泄漏
4、继承
继承权限
继承中的构造与析构
菱形继承
1、C++基础
IO流
#include <iostream>
#include <iomanip> // 对于setprecision, setw等
#include <string>
#include <fstream> // 对于文件操作
using namespace std;
int main()
{
int num;
cin >> num; // 输入流
if (cin.fail())
{
cerr << "input not right"; // 标准err输出
}
cout << num << endl; // 标准输出
// 二进制文件读写
ofstream binfile("test.bin", ofstream::out | ofstream::binary);
char in[] = "A";
binfile.write(in, 1);
binfile.put('B');
binfile.close();
ifstream ifile("test.bin", ofstream::in | ofstream::binary); // 直接构造也可以
ifile.seekg(0, ifile.end); // 跳转到文件末尾
int length = ifile.tellg(); // 获取当前字符在文件当中的位置,即文件的字符总数
ifile.seekg(0, ifile.beg); // 重新回到文件开头
char data[1024];
ifile.read(data, length); // 将文件当中的数据全部读取到字符串data当中
cout << data << endl;
ifile.close(); // 关闭文件
ofstream outFile("example.txt"); // 只读,创建 ofstream 对象
if (!outFile)
{
std::cerr << "Unable to open file for writing";
return 1; // 返回错误代码
}
outFile << "hello" << endl;
outFile << "test" << endl;
outFile.close();
ifstream input("example.txt"); // 只读
if (!input)
{
cerr << "Unable to open file";
return 1;
}
std::string line;
while (std::getline(input, line))
{
std::cout << line << std::endl;
}
input.close(); // 关闭文件
fstream file("example.txt", std::ios::in | std::ios::out | std::ios::app);
file << "\nAppending this line to the file." << std::endl;
file.seekg(0, std::ios::beg);
// 从文件读取数据并输出到控制台
std::string line1;
while (std::getline(file, line1))
{
std::cout << line1 << std::endl;
}
file.close(); // 关闭文件
return 0;
}
namespace
#include <iostream>
using namespace std;
namespace A
{
int a=1;
} // namespace A
namespace B
{
int a=2;
namespace C
{
struct T
{
int a=4;
char c;
};
int a=3;
} // namespace C
}; // namespace B
int main()
{
cout<<"A::a "<<A::a<<"; B::a "<<B::a<<"; C::a "<<B::C::a<<endl;
B::C::T t1;
cout<<t1.a<<endl;
using B::C::T;
T t2;
cout<<t2.a<<endl;
bool F=true;
cout<<boolalpha<<F<<endl; //noboolalpha 输出1
int a=1,b=2,c=3;
auto max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);//三目运算
(a<b? a : b)= 30;//C中的三目运算符返回的是变量值,不能作为左值使用.而C++则可以在任意地方
return 0;
}
引用、const
#include <iostream>
using namespace std;
struct A
{
int a;
char c;
};
void show(A *p)
{
cout << "show func 指针调用前" << endl;
cout << p->a << "\t" << p->c << endl;
p->a = 20;
cout << "show fun 指针修改调用后" << endl;
cout << p->a << "\t" << p->c << endl;
}
void show1(A &p)
{
cout << "show1 引用调用前" << endl;
cout << p.a << "\t" << p.c << endl;
p.a = 35;
cout << "show1 引用修改调用后" << endl;
cout << p.a << "\t" << p.c << endl;
}
void show2(A p)
{ // 临时变量实参的一份拷贝,修改对实参没有影响
cout << "show2 传值调用前" << endl;
cout << p.a << "\t" << p.c << endl;
p.a = 45;
cout << "show2 传值修改调用后" << endl;
cout << p.a << "\t" << p.c << endl;
}
// 指针所指向的内存空间,不能被修改
int operA(const A *p)
{
// p->a=10;
return 0;
}
// 指针变量本身不能被修改
int operatorA1(A *const pT)
{
pT->a = 10;
// pT = NULL; //
return 0;
}
// 指针变量和所指的值都不可以改变
int operatorA2(const A *const pT)
{
// pT->a = 10;
// pT = NULL; //
cout << "A!" << endl;
return 0;
}
// 数据交换, 值交换无法完成
void myswap(int i, int j)
{
int c = 0;
c = i;
i = j;
j = c;
}
// 数据交换, 指针直接对实参修改
void myswap1(int *i, int *j)
{
int c = 0;
c = *i;
*i = *j;
*j = c;
}
// 数据交换, 引用交换,引用做函数参数时不用初始化
void myswap2(int &i, int &j)
{
int c = 0;
c = i;
i = j;
j = c;
}
// 返回a的本身 a的副本
int getA1()
{
int a = 10;
return a;
}
/*想让引用返回可以用就需要将变量生命为static的
/*当函数返回值为引用时若返回栈变量不能成为其它引用的初始值
不能作为左值使用若返回静态变量或全局变量可以成为其他引用的初始值
即可作为右值使用,也可作为左值使用*/
int &getA2()
{
int a = 20;
// warning: reference to local variable ‘a’ returned [-Wreturn-local-addr]
return a;
}
int *getA3()
{
int a = 30;
// warning: reference to local variable ‘a’ returned
return &a;
}
int main()
{
// const operatorA*
const int x = 0;
int const y = 2;
// x=3; y=2;//两个定义一样的效果,修改报错assignment of read-only variable 'x'
// int const z; //未被初始化 error: uninitialized 'const z
// 引用
int var = 10;
int &temp = var; // 引用需要初始化,像一个常量,有自己的空间sizeof与指针一样
temp = 12; // 变量的别名
cout << "var: " << var << " " << temp << endl; // 修改引用同修改本身 12 12
int temp1 = 10, temp2 = 110;
cout << "berfor myswap temp1: " << temp1 << " temp2: " << temp2 << endl;
myswap(temp1, temp2); // 数据交换, 值交换无法完成
cout << "after myswap temp1: " << temp1 << " temp2: " << temp2 << endl;
myswap1(&temp1, &temp2); // 指针传入交换
cout << "after myswap temp1: " << temp1 << " temp2: " << temp2 << endl;
myswap2(temp1, temp2); // 引用的本质时间接修改实参,编译器创造了指针然后修改
// 间接赋值的三个条件,1)定义两个变量,2)变量之间建立关联,3)形参间接修改实参;引用是后两个条件合二为一
cout << "after myswap temp1: " << temp1 << " temp2: " << temp2 << endl;
cout << " ----复杂类型引用 学习---- " << endl;
A t = {101, 'A'}; // 结构体 列表初始化
cout << "main调用指针调用前" << endl;
cout << t.a << "\t" << t.c << endl; // 101 A
show(&t);
cout << "main调用指针调用后" << endl;
cout << t.a << "\t" << t.c << endl;
show1(t);
cout << "main引用调用后" << endl;
cout << t.a << "\t" << t.c << endl;
show2(t);
cout << "main传值调用后" << endl;
cout << t.a << "\t" << t.c << endl;
cout << "------函数返回值是一个引用-------" << endl;
int ga = 1;
int ga1 = 2;
ga = getA1();
// ga1=getA2(); //引用相当于指针,临时变量返回易出错。因为值被释放了
// int &ga2=getA2(); //栈变量返回不能作为初始值
cout << "ga: " << ga << endl;
int *p = NULL; // 指针可以为null
// p = getA3();
// cout << *p << endl; // 证明了引用和指针一样,返回临时变量的指针,有可能被释放了再去使用出现异常
return 0;
}
inline、函数参数
#include <iostream>
using namespace std;
#define MYFUNC(a,b)((a)<(b)?(a):(b))
inline void printA()
{
int a = 10;
cout << "a:" << a << endl;
}
inline int Myfun(int a,int b){
return a<b?a:b;
}
struct A
{
char c;
int a;
};
void show(const A &m)
{
// m.c=10; // error: assignment of member 'A::c' in read-only object
cout << m.a << endl;
}
//函数占位参数 函数调用时,必须写够参数
void func1(int a,int b, int){
cout<<"a: "<<a<<" b: "<<b<<endl;
}
//默认参数与占位参数
void func2(int a,int b, int=0){
cout<<"a: "<<a<<" b: "<<b<<endl;
}
// void func3(int a=0,int b){ 在默认参数规则 ,如果默认参数出现,那么右边的都必须有默认参数
// }
int main()
{
int a = 10;
int &b = a; // 普通引用必须初始化
// int &c=10; //字面值不可以,引用要取地址
int x = 12;
const int &y = x; // 让变量引用只读属性不能通过y修改x
// y=20; 不能修改, x可以改变
x = 13; //修改值y也变了
// 常引用的初始化可以用变量或者常量值
{
const int a = 10;
// int &m=11;
const int &m = 40;
}
A t = {'1', 1};
show(t);
cout<<" ----inline--------"<<endl;
printA();//与下相同
// {
// int a = 10;
// cout<<"a"<<a<<endl;
// }
C++编译器直接将函数体插入在函数调用的地方
//内联函数省去了普通函数调用时压栈,跳转和返回的开销
int a1=1, b1=3;
int c=MYFUNC(a1,b1);
int d=Myfun(a1,b1);
cout<<" define: a1: "<<a1<<" b1: "<<b1<<" c: "<<c<<endl;
cout<<" inlice: a1: "<<a1<<" b1: "<<b1<<" d: "<<d<<endl;
//int c1=MYFUNC(++a1,b1); //a=3 b=3 c=3
//==>宏替换并展开 ((++a) < (b) ? (++a) : (b)) //a=3 b=3 c=3
// cout<<" define ++: a1: "<<a1<<" b1: "<<b1<<" c1: "<<c1<<endl;
int d1=Myfun(++a1,b1); //a=2 b=3 c=2
cout<<" inlice ++: a1: "<<a1<<" b1: "<<b1<<" d: "<<d1<<endl;
func1(a,b,c); //占位符必须有参数
func2(a,b); //ok
func2(a,b,c); //ok
return 0;
}
重载
#include <iostream>
using namespace std;
int func(int x)
{
return x;
}
int func(int x, int y)
{
return x + y;
}
// 函数重载 和 函数默认参数 在一起
// int func(int x, int y, int c = 0)
// {
// return x + y + c;
// }
auto func(const string &s)
{
return s.size();
}
// void func(int x,int y){ //函数返回值不是函数重载的判断标准
// x= x+y;
// cout<<x<<endl;
// }
void func(string *s1, string *s2)
{
cout << *s1 << "\t" << *s2 << endl;
}
// 定义一个函数类型
typedef int(myfun)(int a, int b);
myfun *f = nullptr; // 定义函数指针,指向函数入口
// 声明一个函数指针类型
typedef int (*myfunc1)(int a, int b);
//myfunc1 fp = nullptr;
// 定义一个函数指针 变量
void (*myVarPFunc)(int a, int b);
int main()
{
int a = 0;
a = func(1);
cout << "func a: " << a << endl;
// a=func(1,2); //重载与默认参数在一起产生了二义性
cout << "func1 a: " << a << endl;
cout << "func2 a: " << func("abcd") << endl;
string s = "abcde";
string s1 = "hjic";
func(&s, &s1);
myfunc1 fp;
fp = func;
cout<<fp(1,3)<<endl;
return 0;
}
2、类和对象
类举例
#include <iostream>
#include <string>
class Student {
private:
std::string name; // 学生姓名
int age; // 学生年龄
public:
// 默认构造函数
Student() : name("Unknown"), age(0) {
std::cout << "Default constructor called.\n";
}
// 带参数的构造函数
Student(const std::string& name, int age) : name(name), age(age) {
std::cout << "Parameterized constructor called for " << name << ".\n";
}
// 拷贝构造函数
Student(const Student& other) {
this->name = other.name; // 使用 this 指针
this->age = other.age; // 使用 this 指针
std::cout << "Copy constructor called for " << this->name << ".\n";
}
// 拷贝赋值运算符
Student& operator=(const Student& other) {
if (this != &other) { // 防止自我赋值
this->name = other.name; // 使用 this 指针
this->age = other.age; // 使用 this 指针
std::cout << "Copy assignment operator called for " << this->name << ".\n";
}
return *this; // 返回当前对象的引用
}
// 析构函数
~Student() {
std::cout << "Destructor called for " << name << ".\n";
}
// 显示学生信息
void display() const {
std::cout << "Name: " << name << ", Age: " << age << "\n";
}
};
int main() {
// 使用默认构造函数
Student student1;
student1.display();
// 使用带参数的构造函数
Student student2("Alice", 20);
student2.display();
// 使用拷贝构造函数
Student student3 = student2; // 调用拷贝构造函数
student3.display();
// 使用拷贝赋值运算符
Student student4;
student4 = student2; // 调用拷贝赋值运算符
student4.display();
return 0;
}
3、 内存管理
new/delete
#include <iostream>
#include <cstdlib> // 包含 malloc 和 free 的头文件
class MyClass {
public:
MyClass() {
std::cout << "MyClass constructor called.\n";
}
~MyClass() {
std::cout << "MyClass destructor called.\n";
}
};
int main() {
// 使用 new 和 delete
std::cout << "Using new and delete:\n";
MyClass* obj1 = new MyClass(); // 使用 new 分配内存
delete obj1; // 使用 delete 释放内存
// 使用 malloc 和 free
std::cout << "\nUsing malloc and free:\n";
// 注意:malloc 只分配内存,不调用构造函数
MyClass* obj2 = (MyClass*)malloc(sizeof(MyClass)); // 使用 malloc 分配内存
if (obj2) {
new (obj2) MyClass(); // 手动调用构造函数
}
// 使用 free 释放内存,但需要手动调用析构函数
if (obj2) {
obj2->~MyClass(); // 手动调用析构函数
free(obj2); // 使用 free 释放内存
}
return 0;
}
对象内存分布
#include <iostream>
#include <string>
class MyClass {
public:
int value;
MyClass(int v) : value(v) {
std::cout << "constructor called for value: " << value << "\n"; //10
}
~MyClass() {
std::cout << "destructor called for value: " << value << "\n";
}
};
// 全局变量,存储在数据区
MyClass globalObj(10);
int main() {
// 栈区对象
MyClass stackObj(20);
std::cout << "Address of stackObj: " << &stackObj << "\n"; //10
// 堆区对象
MyClass* heapObj = new MyClass(30);
std::cout << "Address of heapObj: " << heapObj << "\n";
// 数据区对象
std::cout << "Address of globalObj: " << &globalObj << "\n";
// 程序区(代码区)对象
std::cout << "Address of main function: " << (void*)main << "\n";
// 释放堆区对象
delete heapObj;
/*
constructor called for value: 10
constructor called for value: 20
Address of stackObj: 0x6963bffbd4
constructor called for value: 30
Address of heapObj: 0x22296241640
Address of globalObj: 0x7ff6cc537030
Address of main function: 0x7ff6cc531450
destructor called for value: 30
destructor called for value: 20
destructor called for value: 10
*/
return 0;
}
内存泄漏
#include <iostream>
class MyClass {
public:
MyClass() {
std::cout << "MyClass constructor called.\n";
}
~MyClass() {
std::cout << "MyClass destructor called.\n";
}
};
void createMemoryLeak() {
MyClass* obj = new MyClass(); // 动态分配内存
// 忘记释放内存,导致内存泄漏
}
void createNoMemoryLeak() {
std::unique_ptr<MyClass> obj = std::make_unique<MyClass>(); // 使用智能指针
// 不需要手动释放内存,智能指针会自动管理
}
int main() {
for (int i = 0; i < 5; ++i) {
createMemoryLeak(); // 每次调用都会造成内存泄漏
}
std::cout << "Memory leak has occurred.\n";
for (int i = 0; i < 5; ++i) {
createNoMemoryLeak(); // 不会造成内存泄漏
}
return 0;
}
4、继承
继承权限
#include <iostream>
using namespace std;
// 继承
// public 修饰的成员变量 方法 在类的内部 类的外部都能使用
// protected: 修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ;其他 类的外部不能被使用
// private: 修饰的成员变量方法 只能在类的内部使用 不能在类的外部
// 这几个修饰符用在继承里影响的是子类的访问全限
// 类型兼容性原则
class Parent
{
public:
void show()
{
cout << " show 父类" << endl;
a = 0;
b = 1;
c = 2;
cout << "a: " << a << " b: " << b << " c: " << c << endl;
}
int a;
Parent()
{
cout << " --父类 无参 构造 --" << endl;
}
Parent(const Parent &p)
{
cout << " 父类 拷贝 构造--- " << endl;
}
protected:
int b;
private:
int c;
};
class Child1 : public Parent
{
private:
int c1; // 子类有自己的属性
public:
int a1;
void show1()
{ // 访问控制
a1 = 1;
b1 = 2;
c1 = 3;
cout << " show child1 子类1" << endl;
cout << a << b << a1 << b1 << c1 << endl;
//<< c 在子类范围内父类里的属性不变,c是私有已经是类外了
}
protected:
int b1;
};
class Child2 : protected Parent
{
public:
int a2;
void show2()
{ // 访问控制 protected继承,public变成protected,
a2 = 2;
b2 = 4;
c2 = 4;
cout << " child2 子类2" << endl;
cout << a << b << a2 << b2 << c2 << endl;
//<< c 在子类范围内父类里的属性不变,c是私有已经是类外了
}
protected:
int b2;
private:
int c2; // 子类有自己的属性
public:
};
class Child3 : private Parent
{
public:
int a3;
void show3()
{ // 访问控制 私有继承,全变私有,
a = 0;
b = 1;
a3 = 4;
b3 = 4;
c3 = 5;
cout << " child3 子类3" << endl;
cout << a << b << a3 << b3 << c3 << endl;
//<< c 在子类范围内父类里的属性不变,c是私有已经是类外了
}
protected:
int b3;
private:
int c3;
};
void howShow(Parent *base){ //指针
base->show();//父类成员函数
}
void howShow1(Parent &base){ //引用
base.show();//父类成员函数
}
int main()
{
Child1 cl;
// 子类继承了父类的所有属性和方法
cout << " 公有继承 " << endl;
cl.a = 1; // public ,类外可以
// 全是类外不可以访问
// cl.b=1;
// cl.c=1;
// cl.a1=2;
// cl.b1=2;
// cl.c1=3;
cl.show1();
cout << " 保护共有继承 " << endl;
Child2 c2;
// 子类继承了父类的所有属性和方法
// c2.a = 1; //变protected ,类外可以
// 全是类外不可以访问
// c2.b=1;
// c2.c=1;
// c2.a2=2;
// c2.b2=2;
// c2.c2=3;
c2.show2();
Child3 c3;
// 私有更不用说了
c3.show3();
Parent p1;
p1.show();
//基类指针指向子类
Parent *p=nullptr;
p=&cl;
p->show();
//指针做函数参数
howShow(p);
howShow(&cl);
//引用做函数参数
howShow1(p1);
howShow1(cl);
//第二层含义
//可以让子类对象 初始化 父类对象
//子类就是一种特殊的父类
Parent p3 = cl;
return 0;
}
继承中的构造与析构
#include <iostream>
#include <cstring>
using namespace std;
/*
1、子类对象在创建时会首先调用父类的构造函数
2、父类构造函数执行结束后,执行子类的构造函数
3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
4、析构函数调用的先后顺序与构造函数相反
*/
class Object
{
public:
Object(int a, int b)
{
this->a = a;
this->b = b;
cout<<"object构造函数 执行 "<<"a"<<a<<" b "<<b<<endl;
}
~Object()
{
cout<<"object析构函数 \n";
}
protected:
int a;
int b;
};
class Parent : public Object
{
public:
Parent(int p) : Object(1, 2)
{
this->p = p;
cout<<"父类构造函数..."<<p<<endl;
}
~Parent()
{
cout<<"析构函数..."<<p<<endl;
}
void printP(int a, int b)
{
cout<<"我是爹..."<<endl;
}
protected:
int p;
};
class child : public Parent
{
public:
child(int p) : Parent(p) , obj1(3, 4), obj2(5, 6)
{
this->p = p;
cout<<"子类的构造函数"<<p<<endl;
}
~child()
{
cout<<"子类的析构"<<p<<endl;
}
void printC()
{
cout<<"我是儿子"<<endl;
}
protected:
int p;
Object obj1;
Object obj2;
};
菱形继承
#include <iostream>
// 基类
class Base {
public:
void show() {
std::cout << "Base class show function called.\n";
}
};
// 派生类 A
class DerivedA : public Base {
public:
void show() {
std::cout << "DerivedA class show function called.\n";
}
};
// 派生类 B
class DerivedB : public Base {
public:
void show() {
std::cout << "DerivedB class show function called.\n";
}
};
// 最终派生类 C
class DerivedC : public DerivedA, public DerivedB {
public:
void show() {
std::cout << "DerivedC class show function called.\n";
}
};
int main() {
DerivedC obj;
// 调用 DerivedC 的 show 方法
obj.show(); // 输出 DerivedC 的 show 方法
// 访问基类的 show 方法
obj.DerivedA::show(); // 明确调用 DerivedA 的 show 方法
obj.DerivedB::show(); // 明确调用 DerivedB 的 show 方法
return 0;
}
在菱形继承中,DerivedC 通过 DerivedA 和 DerivedB 都继承了 Base 类的成员。如果不明确指定,编译器无法确定应该调用 DerivedA 还是 DerivedB 中的 Base 类成员,这就导致了二义性。
如果加virtual就可以访问基类的 show 方法 obj.Base::show(); // 通过虚继承,只有一个 Base 的实例 。