文章目录
零、学习视频
1.C++最全宝典:黑马程序员
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
2.C++速成复习视频
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 这样的调试器可以帮助定位发生段错误的准确位置。