一、C++介绍
- C++语言是对C语言的扩展和增强:面向对象(
面向过程)、通用算法(泛型编程)
二、C标准库-注释-条件编译
C移植到C++
- C++中包含了C标准库的移植版本,C标准库的头文件x.h基本上变成了cx.
- 但也有列外,如malloc.h没有变
注释和条件编译
三、C++标准输入输出+名字空间
C++标准输入输出
- #include <iostream>为C++标准输入输出的头文件
- cout是一个标准输出流变量(对象),代表控制台窗口
- <<是一个运算符,假如o是一个输出流对象。x是一个数据,o<<x
标准名字空间std
- cout是标准名字空间std的一个名字。必须加上名字空间限定std::,std::cout
- 也可用 using std::cout
- 也可引入名字空间 using namespace std
namespace
namespace exp{
int a,b;
}
四、引用变量、引用形参
引用变量
- 引用变量是其他变量的别名
- 既然是引用,定义引用变量时就必须指明其引用的是哪个变量
- 编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
int a = 3;
int &r = a;
int &r = a;
int &r = b;
- 引用变量和被引用的变量类型必须匹配
- 对引用变量的操作就是对它引用的变量的操作
引用形参
- 使用引用参数可以直接操作实参变量,从而能够实现通过修改形参的值而达到修改对应实参值得目的。
五、函数的默认形参和函数重载
函数的默认形参
void print(char ch, int n = 1);
add(int x = 1,int y,int z = 3);
add(int y,int x = 1,int z = 3);
函数重载
- C++允许同一个作用域里有同名函数,只要它们形参不同。如:
int add(int x,int y);
double add(float x,float y);
- 函数名和形参列表构造了函数的签名
- 函数重载不能根据返回类型区分函数。如
int add(int x,int y);
double add(int x,int y);
六、函数模板
- 通用算法(泛型算法):函数模板->用来产生函数
- 用template关键字增加一个模板头,将数据类型变成类型模板参数
template<typename T>
T add(T x,T y){
return x + y;
}
- 给模板参数传递实际的模板参数,函数模板参数自动推断
template<typename T>
T add(T x,T y){
return x + y;
}
cout << add(5.2,5.3) << endl;
cout << add(5,5) << endl;
double add(double x,double y){
return x + y;
}
int add(int x,int y){
return x + y;
}
七、用户定义类型string和vector
string
- 是一个用户定义类型,表示的是字符串
- 用成员成员访问运算符“.”访问string类成员
string s = "hello",s2("hello");
cout << s.size() << endl;
string s3 = s.substr(1,3);
cout << s3 << endl;
vector
- 向量,类似于数组,但可以动态增长。头文件
- 是一个类模板,实例化产生一个类,如vector产生一个数据元素是int的vector类(向量)
- 同样,可以通过vector类去访问其成员,如成员函数
- 同样可以用运算符进行一些运算
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> v = {7,5,16,8};
v.push_back(25);
v.push_back(13);
}
八、动态内存分配
在C++中使用new在栈上进行内存分配,使用delete释放内存
int* pi = new int;
int* parry = new int[5];
delete pi;
delete []parry;
int *p = new int(10);
C++中的new和C语言中malloc的区别
- C语言中使用malloc时需要包含头文件<malloc.h>
在C++中使用new时不需要包含其他的头文件,因为new是C++中的一部分
new以具体的数据类型进行内存分配,而malloc则是以字节进行内存分配
九、类和对象
面向过程编程
- 传统的过程式编程:变量(对象)就是一些存储数据的内存块,而过程(函数)对这些数据进行处理
- 面向对象编程:程序是由不同种类的许多对象相互协作完成。对象之间通过发送/接收消息来协作完成各做任务。由于这些对象构成的程序也称为“对象式系统”
类和对象
- 类的引入和对象概念
- 在C中,“数据”和“处理数据的操作(函数)”是分开的,C本身不支持“数据和函数”之间的关联性
- C语言中是不允许结构体中定义函数的,而在C++中是允许的,C++可以做到数据和函数产生一定关系的操作,为了区别C语言中结构体的定义struct,C++中常用class来代替struct
- 用 struct或class关键字定义一个类
- 类类型的变量通常称为对象
- 类类型的指针变量
- T是一个类类型,则T*就是T指针类型
- T*变量可以指向一个类对象
- 指向可以指向动态分配的对象
- 类成员函数
- struct和class在C++中的主要区别是默认访问权限。struct默认为public访问权限,class默认为private访问权限。其它功能基本相同。
class student{
string name;
double score;
void print(){
cout<<name<<" "<<endl
};
};
class student{
string name;
double score;
void print();
}
void student::print(){
cout<<name<<" "<<endl
};
十、this指针、访问控制、构造函数
this指针
- this指针的类型:类类型* const
- this指针是一个自动定义的指针,它指向当前对象的地址。当一个对象调用它的成员函数时,编译器会自动将该对象的地址作为一个隐含的参数传递给该成员函数,这个隐含的参数就是this指针。
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针
- this指针用于在类的成员函数中访问类的成员变量和成员函数。在类的成员函数中,this指针可以用来区分局部变量和成员变量,因为局部变量和成员变量的名称可以相同。this指针也可以用来返回当前对象的引用,以便支持链式调用
class MyClass {
public:
void setValue(int value) {
this->value = value;
}
int getValue() {
return value;
}
private:
int value;
};
int main() {
MyClass obj;
obj.setValue(42);
cout << obj.getValue() << endl;
return 0;
}
访问控制
- 公有(public):公有成员可以在类的内部和外部被访问,派生类也可以访问。通常将类的数据成员声明为私有,成员函数声明为公有,以提供对私有数据成员的访问方式
- 私有(private):私有成员只能在类的内部被访问,外部不能访问,派生类也不能访问。私有成员用于实现类的封装,保证类的数据成员不会被外部直接访问
- 保护(protected):保护成员可以被类的内部和派生类访问,但不能被外部访问。通常用于实现继承,使派生类能够访问基类的成员,但外部不能访问基类的成员
class MyClass {
public:
int public_var;
void public_func();
protected:
int protected_var;
void protected_func();
private:
int private_var;
void private_func();
};
构造函数
- 构造函数是一种特殊的成员函数,用于在创建对象时进行初始化操作。它具有与类名称相同的名称,没有返回类型,并且可以具有参数列表。构造函数可以有多个重载形式,以便在创建对象时可以根据需要使用不同的参数
- 构造函数在对象创建时自动调用,因此它可以用于确保对象的正确初始化和设置。如果没有定义构造函数,则编译器将提供一个默认的构造函数,该函数不执行任何操作
class MyClass {
public:
MyClass() {
}
MyClass(int value) {
}
};
int main() {
MyClass obj1;
MyClass obj2(10);
return 0;
}
十一、运算符重载
- C++运算符重载是一种特殊的函数重载,可以将C++内置的运算符用于自定义数据类型。通过运算符重载,我们可以为自定义数据类型定义特定的行为,使其能够像内置数据类型一样使用运算符
- 不能重载的运算符包括:“.” “.*” “::” “sizeof” “?:”
返回类型 operator 运算符(参数列表)
{
}
十二、Stirng类、拷贝构造函数、析构函数
Sting类
- String类是C++中一个常用的字符串类,提供了一系列的字符串操作函数和方法。使用String类时,需要包含头文件。String类的使用方式与C字符串有很大的区别,它可以自动管理内存,不需要手动分配和释放内存
- 常用String类方法
- length():返回字符串长度
- empty():判断字符串是否为空
- assign():把一个字符串赋给另一个字符串
- append():在字符串末尾添加另一个字符串
- insert():在字符串指定位置插入另一个字符串
- erase():删除字符串指定位置的字符
- replace():替换字符串中的某个子串
- find():查找字符串中的某个子串
- substr():返回字符串的子串
拷贝构造函数
- 拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,该对象与现有对象具有相同的值
- 当使用一个对象来初始化另一个对象时,或者将一个对象作为函数参数传递给另一个函数时,拷贝构造函数会被调用
- C++编译器会为每个类生成一个默认的拷贝构造函数,该函数会将一个对象的值复制到另一个对象中。但是,如果类中有指针成员变量,则需要手动编写拷贝构造函数,以确保正确地复制指针指向的数据。
析构函数
- 析构函数是一种特殊的成员函数,用于在对象被销毁时自动执行一些清理工作。它的名称与类名称相同,但前面加上了一个波浪号(~)作为前缀
- 析构函数没有参数,不能被重载,也不能显式调用。它的作用是在对象被销毁时自动调用,进行清理工作,例如释放动态分配的内存、关闭文件或网络连接等
- 如果一个类没有定义自己的析构函数,编译器会自动生成一个默认的析构函数。这个默认的析构函数什么也不做,因此如果在类中使用了动态分配内存等需要清理的资源,就需要自己定义析构函数来释放这些资源
#include <iostream>
using namespace std;
class MyClass {
private:
int size;
int* data;
public:
MyClass(int s) {
size = s;
data = new int[s];
for (int i = 0; i < s; i++) {
data[i] = i;
}
}
MyClass(const MyClass& obj) {
size = obj.size;
data = new int[size];
for (int i = 0; i < size; i++) {
data[i] = obj.data[i];
}
}
~MyClass() {
delete[] data;
}
void printData() {
for (int i = 0; i < size; i++) {
cout << data[i] << " ";
}
cout << endl;
}
};
int main() {
MyClass obj1(5);
obj1.printData();
MyClass obj2 = obj1;
obj2.printData();
return 0;
}
十三、类模板
- 类模板是一种通用的类定义,可以用于创建具有相同结构但可以使用不同类型的对象。类模板定义了一个模板类,其中一个或多个类型参数用作占位符,可以在创建类的实例时用实际的类型值进行替换。类模板可以有成员函数和成员变量,就像普通类一样
template <typename T>
class Stack {
private:
vector<T> elements;
public:
void push(T const& element) {
elements.push_back(element);
}
void pop() {
if (elements.empty()) {
throw out_of_range("Stack is empty");
}
elements.pop_back();
}
T top() const {
if (elements.empty()) {
throw out_of_range("Stack is empty");
}
return elements.back();
}
bool empty() const {
return elements.empty();
}
};
int main() {
MyClass obj1(5);
obj1.printData();
MyClass obj2 = obj1;
obj2.printData();
return 0;
}
int main(){
Stack<int> intStack;
intStack.push(42);
intStack.pop();
}
}
bool empty() const {
return elements.empty();
}
};
int main() {
MyClass obj1(5);
obj1.printData();
MyClass obj2 = obj1;
obj2.printData();
return 0;
}
int main(){
Stack<int> intStack;
intStack.push(42);
intStack.pop();
}