C++面向对象 基础

文章目录

零、学习视频

1.C++最全宝典:黑马程序员

黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难


2.C++速成复习视频

1.c++面向对象编程速成!90分钟搞定

2.复习100分钟拿下100分,你能做得到吗?【C++】(面向对象程序设计)


3.C++语法基础训练(牛客语法题)

牛客:https://www.nowcoder.com/exam/oj?page=1&tab=%E8%AF%AD%E6%B3%95%E7%AF%87&topicId=225


4.C++万能头文件:#include<bits/stdc++.h>

万能头文件:
①优点:省事
②缺点:编译速度慢、编译后的文件体积大

总结:不推荐使用,老老实实自己需要哪个头文件就用哪个


一、标准库函数

1.memset(arr,0,sizeof(arr)):数组初始化

memset常用于初始化,对数组进行清零操作。

memset(B, 0, sizeof(int)*n)//赋初值为0

1.头文件:

#include <cstring> #include <string.h>

2.将数组初始化为0

void *memset(void *ptr, int value, size_t num);
void *memset(void *str, int c, size_t n)

描述:C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

str:指向要填充的内存块。
c:要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
n:要被设置为该值的字符数。

3.size_t是一种无符号整数类型,跨平台。

#include <cstring>

int arr[10][10];
memset(arr, 0, sizeof(arr));

2.memcpy():复制数组

复制数组

void* memcpy(void* dest, const void* src, size_t n);
#include <cstring>
memcpy(dest, src, sizeof(src));

3.max(a,b)

比较两个变量的大小

#include <iostream>
using namespace std;

int main(){
    int a,b;
    while(cin >> a >> b){
        cout << max(a,b);
    }
    return 0;
}

4.getline()

C++读函数,一次读一整行

getline(cin, str);

getline(cin,str) 比起 cin.getline()更安全,可自动扩容。


5.sqrt()

开根函数,头文件为<cmath>,被包含于<algorithm>


6.pow()

幂次函数,头文件为<cmath>

#include <cmath>

int main() {
    int n = 5; 
    int result = std::pow(2, n); //求2的5次方
}

7.isalpha( c):判断c是否为字母

若c为字母,isalpha(c)返回true。若c不为字母,isalpha(c)返回false。

map<char,int> maps;
for(int i = 0; str[i] != '\0'; i++){
    if(isalpha(str[i])){
        maps[str[i]] ++;
    }
}

8.replace() 字符串替换函数


9.resize()

申请内存空间

int size = max(fa.size(),fb.size());
string res;
res.resize(size);

10.sort()

排序函数,时间复杂度为O(nlog₂n)。
用法:
①sort(起始位置,结束位置)
②sort(起始位置,结束位置,排序方法)


11.c_str()

c_str()返回的是一个临时指针,不能对其进行操作。
c_str是Borland封装的String类中的一个函数,它返回当前字符串的首字符地址。


12.字符串搜索:find、rfind

用法:
find("字符串内容")   //默认从位置0开始搜索
find("字符串内容",偏移量n)

rfind("字符串内容")   //默认从位置0开始搜索
rfind("字符串内容",偏移量n)


代码实例:

#include <iostream>
#include <string>

using namespace std;

int main(){
	std::string str1 = "abbasdasdf";
	unsigned long p1 = str1.find("ba,2");//从第2个位置开始正向搜索
	unsigned long p2 = str1.rfind("ba");//从最后一个位置开始倒着搜素
	cout<< p1 <<endl;
	cout<< p2 <<endl;

}



二、数据的输入输出

在这里插入图片描述

1.cin:输入

(1)cin与scanf的区别

cin会忽略空白字符(空格、制表符\t、换行符\n)
scanf不会忽略空白字符

//cin >> data;
scanf("%c",&data);
if(data == '\n') break; //此处上文不可用cin,会忽略空白符

(2)cin.getline():读取一行内容

2.cout:输出

(1)三目运算符

输出a和b中的较大值:

int a,b;
cin>>a>>b;
cout<< (a>b?a:b); 

注意,用cout输出三目运算符,需要整体加括号


(2)计算一个数的阶乘

核心代码:

    while(n){
        factorial *= n;
        n--;
    }

完整代码:

#include <iostream>
using namespace std;

int main() {
    
    int n;
    cin >> n;
    long long factorial = 1;
    
    while(n){ //等价于 while(n != 0)
        factorial *= n;
        n--;
    }
    
    cout << factorial << endl;
    
    return 0;
}



3.I/O格式控制

(1)cout 输出格式控制

设置域宽为5个字符:setw(5)
设置保留小数点后2位有效数字:setprecision(3)

#include <iomanip>
cout<<setw(5)<<setprecision(3)<<3.1415<<endl;

在这里插入图片描述


(2)printf 输出格式控制
①位数(宽度)控制

%2d:宽度为2位
%02d:宽度为2位,不足则用0补足
%.2f:小数点后面2位
%.0f:小数点后面0位,即不保留小数部分,只留整数部分。四舍五入到整数。


②实现四舍五入:%.0f

解法1:C++版本:

#include <iostream>
using namespace std;

int main() {
    float a;
    int b;
    cin>>a;
    //四舍五入
    b = a;
    if(a>0){
        if(a-b>=0.5 )    b++;
    }else{ //a<0
        if(b-a>=0.5 )    b--;
    }
    cout <<b <<endl;
}

解法2:C语言版本:%.0f实现四舍五入

#include <cstdio>

int main(){
    float a;
    scanf("%f",&a);
    printf("%.0f",a);
    return 0;
}

%.0f 是格式输出一个浮点数,去掉小数点后面的数。
%.0f的0代表小数点后面几位,%.2f就是小数点后面2位。
输出浮点数的整数部分,不输出小数点和小数点以下部分。小数部分4舍5入。
%.0f隐式地调用了四舍五入函数 round()


4.按任意键继续:cin.get()

cout << endl << "输入任意键继续..." << endl;
cin.ignore(); //清空缓冲区
cin.get();    //读取一个字符



三、new与delete

new出来是在堆区,需要手动delete,不然会造成内存泄露。

手动申请内存,手动释放内存,称为动态分配

int *p = new int; // int *p = new int[n];
delete p;

delete p 是释放指针p的内存,而不是删除了指针p本身。

void func(){
	int a = 100;
	int *p = new int;
	delete p;
	p = &a;
}

int* ptr = new int();	 // 申请一个int类型的对象,并初始化为0
int* ptr = new int(10);  // 申请一个int类型的对象,并初始化为10
int* arr = new int[10];  // 申请一个大小为10的数组

int* ptr = new int(10); 这行代码不会创建一个数组,而是动态分配内存以存储一个单个的 int 类型的对象,并初始化为10,用指针ptr指向它。


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



四、遍历方式

在这里插入图片描述


1.数组下标

for(int i = 0; i < n; ++i){
	cout << arr[i] << " ";
}
cout << endl;

2.迭代器 iterator

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

C++11引入关键字auto,自动推断变量的类型

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

3.范围for循环 (C++11)

1.值传递

vector<int> vec = {1, 2, 3, 4, 5};
for (int value : vec){    	//value,或可命名为elem
    cout << value << " ";
}
cout << endl;

2.引用传递&

vector<int> vec {1, 2, 3, 4, 5};
for (int &elem : vec) {
    cout << elem << " ";
}
cout << endl;

3.auto + 引用传递

for(auto &elem : vec){   //引用
	cout << elem << " ";
}
cout << endl;

for(auto &c: clients) {
    string name = clients[client_fd].username;
    if(c.first != client_fd){  //不为自己
        write(c.first,('[' + name + ']' + ":" + msg).c_str(),msg.size()+name.size()+3);
    }
}



五、STL

C++迭代器超详细讲解

1.迭代器

1.正向迭代器:iterator

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

2.逆向迭代器:reverse_iterator

vector<int>::reverse_iterator rit;
for(rit = vec.rbegin(); rit != vec.rend(); ++rit){
    cout << *rit << " ";
}

2.vector

1.支持下标运算
vec[k-1] 为第k个元素


3.set

1.查找 find()

if(set3.find(2) == set3.end()){
    printf("2 is not in the set.\n");
}else{
    printf("2 is in the set.\n");
}

2.lower_bound( ) 和 upper_bound( )

头文件:#include \<algorithm>

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

lower_bound(T value):返回一个迭代器,指向第一个大于或等于给定值的元素。如果集合中没有这样的元素,则返回指向集合末尾的迭代器。
upper_bound(T value):返回一个迭代器,指向第一个大于给定值的元素。如果集合中没有这样的元素,则返回指向集合开头的迭代器。

set<int>::iterator it = s.upper_bound(x);
if(it == s.end()){
	cout << -1 << endl;
}else{
	cout << *it << endl;
}

4.map

1.统计某字符出现的次数

mymap[str[i]]++;



六、类和对象

定义类:①数据成员 ②成员函数 ③访问权限

类是创建对象的模板,对象是类的实例
类定义了对象有何种属性和方法,而对象拥有的具体属性则可以不尽相同。


0.面向过程 与 面向对象

面向过程是编年体,面向对象是纪传体


1.struct与class

(1)struct与class的区别

结构体struct和类class都是用来存储多个变量的,两者的用法的差不多,但存在一下区别:
1.类型不同:struct是值类型,而class是引用类型。
2.公有私有:struct中全部成员必须为public,class中默认的成员访问权限是private,可以设置公有还是私有。(没有private的类相当于结构体)
3.能否继承:struct不能被继承,而class可以被继承。

(2)struct与class的相同点

1.C++中,结构体和类中都可以有成员函数



(3)值类型和引用类型

值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。

1.值类型
值类型(value type):byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。值类型变量声明后,不管是否已经赋值,编译器为其分配内存。

2.引用类型
引用类型(reference type):string 和 class统称为引用类型。当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。



2.如何使用class/struct的成员函数:创建对象,调用方法

直接使用类的成员函数会报错。
必须要实例化一个对象,类的对象才能调用类/结构体的成员函数。


错例:

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

struct Student{
    int age;
    string name;
    int add(int a,int b);
};

int Student::add(int a,int b){
    return a+b;
}

int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    cout<<Student::add(a,b);   //这句会报错
    return 0;
}

改正:创建对象,对象调用成员函数

Student stu;
cout<<stu.Student::add(a,b);


3.类的成员函数和成员数据的正规写法

方法放在public中,属性放在private中。防止外部直接修改数据。
在这里插入图片描述


4.public、protected、private的区别

1.public:公开的,所有人均可访问
2.protected:保护的,仅自己和孩子可以访问,即 仅可在 本类 和 派生类 中被访问
3.private:私有的,仅自己(本类)可访问,即仅能被【类中的】其他【成员函数】或【友元函数】所访问

若不想某成员函数和数据成员被类外的对象所访问,可以将其私有化,即放入private



七、成员函数

在这里插入图片描述


1.(普通)构造函数(Constructor):对象的创建

1.构造函数的本质:每个对象的默认初始化。
创建对象的时候会调用构造函数。如果每个对象都需要相同的初值,若将初值写在构造函数里,就不必为每个对象显式地写初值了。

不带参数的构造函数、带参数的构造函数:
创建对象时不带参数,调用不带参数的构造函数。初始值赋予默认值;
创建对象时对象有括号带参数,则调用带参数的构造函数。初始值一般赋予传进去的参数。


举例:

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

class Person{
public:
    //成员变量
    string name;
    int age;
    //成员函数
    Person(string n,int a){  //构造函数名与类同名
        name = n;
        age = a;
    }
};

int main() {
    //使用构造函数创建Person对象
    Person person1("Edward",25); //人类,对象1名为person1,创建有参的对象自动调用有参的构造函数
    Person person2("Amber",25);  //人类,对象2名为person2,创建有参的对象自动调用有参的构造函数
    //打印属性值
    cout << person1.name << " is " << person1.age << " years old." << endl;
    cout << person2.name << " is " << person2.age << " years old." << endl;
    return 0;
}

若传入的参数与成员函数同名,则用this指针进行区分。

//成员函数
Person(string name,int age){  //构造函数名与类同名
    this->name = name;
    this->age = age;
}

复杂的例子:

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

class Student{
public:
	//成员变量
    int age;
    string name;
    //成员函数
    Student(); //无参的构造函数
    Student(int a,string n); //有参的构造函数

};

//无参的构造函数
Student::Student(){
    age = 24;
    name = "Edward";
    cout<<"Student()"<<endl;
}

//有参的构造函数
Student::Student(int a,string n){
    age = a;
    name = n;
    cout<<"Student(age,name)"<<endl;
};

//类继承
class Postgraduate:public Student{
public:
    string reserach;
    Postgraduate();
    Postgraduate(string re);
    Postgraduate(int a,string n,string re);
};

Postgraduate::Postgraduate(){
    reserach = "CS";
    cout<<"Postgraduate()"<<endl;
}

Postgraduate::Postgraduate(string re){
    reserach = re;
    cout<<"Postgraduate(string re)"<<endl;
}

Postgraduate::Postgraduate(int a,string n,string re):Student(a,n){
    reserach = re;
    cout<<"Postgraduate(int a,string n,string re)"<<endl;
};

int main(){
    Postgraduate a("计算机");
    cout<<a.age<<endl<<a.name<<endl<<a.reserach<<endl<<endl;

    Postgraduate c(21,"小王","机械");
    cout<<c.age<<endl<<c.name<<endl<<c.reserach;
    return 0;
}

输出:

Student()
Postgraduate(string re)
24
Edward
计算机

Student(age,name)
Postgraduate(int a,string n,string re)
21
小王
机械



2.拷贝构造函数(Copy Constructor)

1.格式

类名(const 类名 & 对象名)

举例:
(1)类内

Point(const Point & rhs)
B(const B &rhs) 

(2)类外
类外则需要加:类名、作用于限定符

Point::Point(Point &p) {  }

对象也可以无名:匿名对象


2.定义
用赋值的方式,为新对象初始化时,调用拷贝构造函数

Student stu2 = stu1; 

//拷贝构造函数
Array(const Array & p){
	this->n = p.n;
	this->a = new int[n];
	for(int i = 0; i < n; ++i){
		this->a[i] = p.a[i];
	}
}

(1)引用、浅拷贝与深拷贝

1.引用
引用不创建新对象,也不拷贝数据。 是给对象起了个别名。操作新名字等于操作原对象。一个对象,一套地址。

2.浅拷贝
浅拷贝创建新对象,但不拷贝数据,仍指回原地址。 两个对象,一套地址。

3.深拷贝
深拷贝创建新对象,并且开辟内存保存拷贝的数据。 两个对象,两套地址。


拷贝构造函数,分为 浅拷贝构造函数深拷贝构造函数

1.浅拷贝(Shallow Copy):
仅仅复制了变量的值。只修改了指针的指向。浅拷贝可能造成 double free
用括号包着:等价于 赋值。在拷贝构造函数里,只能用括号

2.深拷贝(Deep Copy):
创建一个新的实例,再拷贝

浅拷贝构造函数仅复制 data 指针的值,所以浅拷贝和原始对象共享相同的内存地址。
深拷贝构造函数创建了一个新的 int 实例,并将其地址赋给新对象的 data 指针,确保了深拷贝和原始对象的独立性。


//浅拷贝构造函数
Person(const char* name, int age) {
     this->name = new char[strlen(name) + 1];
     strcpy(this->name, name);
     this->age = age;
}
//深拷贝构造函数
    Person(const Person & p){
       this->name = new char[strlen(p.name) + 1];
       strcpy(this->name, p.name);
       this->age = p.age;
}

举例说明
假设有一个类 MyClass 包含一个指向 int 类型的指针成员:

class MyClass {
public:
    int *data;
    // 构造函数、析构函数等
};

浅拷贝: 如果我们仅仅复制 data 指针的值,那么新对象和原对象的 data 成员将指向相同的内存地址。
深拷贝: 如果我们创建一个新的 int 对象,并将其地址赋值给新对象的 data 成员,那么新对象和原对象的 data 成员将指向不同的内存地址。



(2)软链接、硬链接

1.软链接,相当于 快捷方式。软链接要用绝对路径。用相对路径建立软链接,在软链接soft文件被移动后可能会失效。

2.深拷贝与硬链接的区别
①深拷贝开辟了新空间,有两个空间 (两条蛇)
②硬链接建立了同一个空间的新路径:还是同一个空间,但是有多条路径(九头蛇)



3.析构函数:对象的销毁

1.析构函数的作用:释放堆空间

2.格式:

类名(const 类名 & 对象名)

举例:

Point(const Point & rhs)
B(const B &rhs) 

对象也可以无名:【匿名对象】

3.举例

~Array(){
	delete []a;
}
 ~Person() {
    if (name != nullptr) {
        delete[] name;
        name = nullptr;
    }
}



4.常成员函数 const

安全起见,防止在该函数中出现修改数据的情况。一般用于只读函数。


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

class Student{
public: //声明public,否则class默认private,在main函数中无法调用
    int age;
    string name;
    bool read() const;
    Student();      //构造函数的声明
};

Student::Student(){ //构造函数的实现
    age = 24;
    name = "Edward";
}

bool Student::read() const{  //加const是为了安全起见,防止该函数后续被写入赋值语句
    cout<<"age:"<<age<<endl;
    cout<<"name:"<<name<<endl;
    //age = 25;  //会报错
}

int main(){
    Student stu; //创建对象
    stu.read();
    return 0;
}

在const里修改数据会报错:
【Cannot assign to non-static data member within const member function ‘read’
【在const成员函数read中不能给非静态数据成员赋值】



5.静态成员 static

描述全局,与类有关,又与某个对象属性无关的,叫做静态成员数据。读取静态成员数据的方法,叫做静态成员函数

静态成员数据:静态成员数据只能由静态成员函数读取。
静态成员函数:不依赖于对象。


例:静态成员变量需要在类外进行初始化
int Student::cnt = 0;

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

class Student{
public:
    int age;
    string name;
    Student(); //构造函数
    static int cnt; //统计对象个数
    static int count();
};

Student::Student(){
    age = 24;
    name = "Edward";
    cnt++;
}

int Student::cnt = 0; //静态成员变量需要在类外初始化

int Student::count(){  //这里不写static
    return cnt;
}

int main(){
    Student a;
    Student b;
    cout<<Student::count();
    return 0;
}



八、友元

友元,破坏了类的封装性和隐藏性


1.友元函数

(1)概念
 friend 函数类型 函数名(形式参数);

友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。


(2)例题
例题1:CPP42 友元全局函数

提交网址:https://www.nowcoder.com/share/jump/2891302591709689971285

在这里插入图片描述

friend void showAge(Person &); //或 Person &p
#include <iostream>
using namespace std;

class Person {
    // write your code here......
    friend void showAge(Person &); //或 Person &p

    public:
        Person(int age) {
            this->age = age;
        }

    private:
        int age;
};

void showAge(Person& p) {
    cout << p.age << endl;
}

int main() {

    Person p(10);
    showAge(p);

    return 0;
}

2.友元类

(1)概念

(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。


(2)例题
例题1:CPP63 友元类

提交网址:https://www.nowcoder.com/share/jump/2891302591709690226252

在这里插入图片描述

public:
		friend class myphone; //友元声明最好放在public中
#include<bits/stdc++.h>
using namespace std;
class phone{
	// write your code here......
	private:
		int price;
	public:
		friend class myphone; //友元声明最好放在public中
		phone(int x){
			price=x;
		}
}; 
class myphone{
	private:
		phone a;
	public:
		myphone(int x):a(x){
		}
		int getprice(){
			return a.price;
		}
};
int main(){
	int p;
	cin>>p;
	myphone a(p);
	cout<<a.getprice();
	return 0;
}



九、重载

1.运算符重载

(1)意义

为了能让运算符也能操作对象,为对象重新定义运算符函数。

①为了能让对象之间也能实现四则运算等操作:对象不能直接+ - * / << >>,因此要为对象间的加减乘除运算定义相关的函数。即是运算符重载函数operator +、operator-等等
②重载(再次定义同名的函数)运算符函数,把运算符函数的其中一个或者几个参数改为对象


(2)要求

参数个数、返回值类型,不变。
函数名为 operator 符号名

//复数
struct Complex{
    int realpart;
    int imaginarypart;
};

bool operator < (Complex lhs,Complex rhs){
	return pow(lhs.realpart,2) + pow(lhs.imaginarypart,2) < pow(rhs.realpart,2)+pow(rhs.imaginarypart,2);
}

(3)例题
例题1:CPP43 加号运算符重载

提交网址:https://www.nowcoder.com/share/jump/2891302591709694111120

在这里插入图片描述


例题2:重载小于号

提交网址:https://www.nowcoder.com/share/jump/2891302591709694156525

在这里插入图片描述



2.函数重载

函数重载:相同函数名和返回值,但参数不同。称为函数重载,只有C++支持,C语言并不支持函数重载。

#include <stdio.h>
//#include <cstdio>
//#include <iostream>
//using namespace std;

int ret(int a){
    return a;
}

int ret(int a,int b){ //函数重载
    return a+b;
}

int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%d ",ret(a));
    printf("%d ",ret(b));
    printf("%d\n",ret(a,b)); 
    return 0;
}

输入:1 2
输出:1 2 3



十、抽象、封装

1.概念

1.抽象:一个个的对象个体,抽象出来,就形成了类。实例抽象为对象,对象的集合为类。

2.封装:封装 成员函数和数据成员
成员(成员函数和数据成员)不想被类外访问,私有化(放入private)。但友元打破了封装性。

“封装”思想:黑箱,①模块化 ②保密
封装能减少我们对不必要细节的精力投入。


2.例题

例题1:CPP38 设计立方体类

提交网址:https://www.nowcoder.com/share/jump/2891302591707619311701

在这里插入图片描述

#include <iostream>
using namespace std;

class Cube {
private:  
    int length;
    int width;
    int height;
public:
    void setLength(int length){
        this->length = length;
    }
    void setWidth(int width){
        this->width = width; 
    }
    void setHeight(int height){
        this->height = height;
    }
    int getLength(){
        return this->length;
    }
    int getWidth(){
        return this->width;
    }
    int getHeight(){
        return this->height;
    }
    int getArea(){ //表面积
        return (this->length*this->width + this->length*this->height + this->width*this->height)*2;
    }
    int getVolume(){
        return this->length*this->width*this->height;
    }
};

int main() {
    int length, width, height;
    cin >> length >> width >> height;

    Cube c;
    c.setLength(length);
    c.setWidth(width);
    c.setHeight(height);

    cout << c.getLength() << " "
        << c.getWidth() << " "
        << c.getHeight() << " "
        << c.getArea() << " "
        << c.getVolume() << endl;
    return 0;
}



十一、继承

1.子类会自动继承父类的属性和方法。只需要写继承方式和自己新增的成员。
继承时,会首先调用基类的构造函数,再调用派生类的构造函数

在这里插入图片描述

2.继承方式:
①public:所有人都能访问
②protected:只有自己和子类能访问
③private:只有自己能访问
在这里插入图片描述



1.书写格式

1.派生类继承基类,语法如何写?
答:基类写在派生类后面

class 派生类名 : 继承方式 基类名 {
public / protected /private
数据成员 成员函数 }

#include<iostream>
using namespace std;
//基类People
class People{
public:
    void show();
protected:
    char *m_name;
    int m_age;
};
void People::show(){
    cout<<"嗨,大家好,我叫"<<m_name<<",今年"<<m_age<<"岁"<<endl;
}
//派生类Student
class Student: public People{
public:
    Student(char *name, int age, float score);
public:
    void show();  //遮蔽基类的show()
private:
    float m_score;
};
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
int main(){
    Student stu("小明", 16, 90.5);
    //使用的是派生类新增的成员函数,而不是从基类继承的
    stu.show();
    //使用的是从基类继承来的成员函数
    stu.People::show();
    return 0;
}

运行结果

小明的年龄是16,成绩是90.5
嗨,大家好,我叫小明,今年16

例2:

class 派生类名 : 继承方式 基类名 {
public / protected /private
数据成员 成员函数 }

class Animal{
	protected:
		char *name;
		int age;
	pubilc:
		Animal(char* n,int a){
			name = new char[strlen(n)+1];
			strcpy(name,n);
			age = a;
		}
		virtual void speak(){};
		virtual ~Animal(){
		if(name!=NULL)
			delete name;
		}
};

class Tiger:public Animal{
	public:
		Tiger(char *n,int a):Animal(n,a){}
		virtual void speak(){
			cout<<"Hello,I am "<<name<<",Aooo."<<endl;
		}
};

class Dog:public Animal{
	public:
		Dog(char *n,int a):Animal(n,a){}
		virtual void speak(){
			cout<<"Hello,I am "<<name<<",WangWang."<<endl;
		}
};



2.继承,又称 特例化

子类继承父类:特例化
类创建对象:实例化



十二、多态

1.覆盖

1.重载:同返回值、同名的函数,却有不同的参数。在C++中当作不同的函数
2.隐藏(Hiding):父类与子类函数同名,可以有不同参数,父类的该函数被隐藏,默认调用子类的,而看不见父类的
3.覆盖(Overriding):子类与父类有同名的虚函数。

重载是编译时决定,多态是运行时决定。

在这里插入图片描述

在这里插入图片描述


2.虚函数、类指针

virtual

在这里插入图片描述


3.纯虚函数、抽象类

在这里插入图片描述
在这里插入图片描述



十三、宏 (宏命令)

1.INT_MAX:C++中表示+∞的宏。需要加入头文件

#include <climits>



十四、C++内存:堆内存区、栈内存区

在这里插入图片描述



十五、C++关键字

1.static

static 声明 静态变量。函数体内部的静态变量不会随着函数的消亡而消亡,而是一直存在。类似于全局变量。

2.const

const声明 常量。被const 修饰的变量从此变为常量,其值不能再改变。若const修饰的是指针,则该指针变为指针常量,其指向不能再改变。

3.this

this 关键字是指向当前对象的指针,它允许访问当前对象的成员变量

4.virtual

virtual 声明 虚函数

5.friend

友元

6.operator

运算符重载

7.union

union 联合,是一种特殊的类。可以联合其内部不同类型的数据结构。



十六、常见编程错误

1.段错误:SIGSEGV

1.名称解释
SIGSEGV,指Segmentation Violation,即段错误(segmentation fault)

2.原因
SIGSEGV 是一个信号,表示在 Unix 或类 Unix 操作系统中发生了段错误(segmentation fault)。这通常发生在程序尝试访问它没有权限访问的内存区域时。换句话说,当程序试图读取或写入一个它不应该访问的内存地址时,就会发生 SIGSEGV 错误。

3.分类
SIGSEGV 错误常见于以下情况:
解引用空指针:尝试访问一个空(NULL 或 nullptr)指针所指向的内存。指针访问了没有分配地址的空间,或者指针为NULL
数组越界:访问数组时超出了其分配的内存范围。
非法内存访问:尝试访问已释放的内存,或者一个未初始化或未分配的内存地址。
栈溢出 (Stack Overflow):函数调用过深,导致栈空间耗尽。如递归没有递归出口等。

4.解决
处理 SIGSEGV 错误通常涉及调试程序,检查指针操作和内存分配,以确定并修正导致非法内存访问的代码。使用如 GDB 这样的调试器可以帮助定位发生段错误的准确位置。



十七、文件操作

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员爱德华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值