使用命名空间之后,调用代码时可以省去也可以不省去相关的前缀。
#include
using namespace std;//使用c++自己的命名空间
int main() {
int num1 = 10;
std::cout << "Hello, World!" << std::endl;
cout<<num1<<endl;
return 0;
}
自定义命名空间
// 命名空间
#include
// 声明std,我们的main函数就可以直接使用里面的成员,不需要使用 std::
using namespace std; // C++自己的命名空间 (C# .net 命名空间)
// 自定义命名空间
namespace derry1 {
int age = 33;
char * name = "Derry猛男1";
void show() {
cout << "name:" << name << ", age:" << age << endl;
}
void action() {
cout << "derry1 action" << endl;
}
}
// TODO ------ 命名空间里面重复的函数
// 自定义命名空间
namespace derry2 {
void action() {
cout << "derry2 action" << endl;
}
}
// TODO ------ 小概率会遇到的情况,命名空间的嵌套
// 自定义命名空间
namespace derry3 {
namespace derry3Inner {
namespace derry3Inner1 {
namespace derry3Inner2 {
namespace derry3Inner3 {
void out() {
cout << "爱恨情仇人消瘦,悲欢起落人寂寞" << endl;
}
}
}
}
}
}
// 声明各个写的 命名空间
// using namespace derry1;
int main() {
cout << "命名空间" << endl;
// 声明各个写的 命名空间
using namespace derry1;
int ageValue = derry1::age; // 方式1 使用 刚刚声明的命名空间
derry1::show(); // 使用 刚刚声明的命名空间
ageValue = age; // 方式2 直接去引出来 ::
show(); // 直接去引出来 ::
// TODO ------ 命名空间里面重复的函数
using namespace derry2;
// action(); 很尴尬
derry1::action();
derry2::action();
// TODO ------ 小概率会遇到的情况,命名空间的嵌套
// 第一种方式 先声明命名空间 再使用
using namespace derry3::derry3Inner::derry3Inner1::derry3Inner2::derry3Inner3;
// 再使用
out();
// 第二种方式 直接使用
derry3::derry3Inner::derry3Inner1::derry3Inner2::derry3Inner3::out();
return 0;
}
//
// Created by sunteng on 2023/9/5.
//
#ifndef TEST_STUDENT_H
#define TEST_STUDENT_H
#endif //TEST_STUDENT_H
class Student{
private:
//下面的成员和函数,都是私有
char * name;
int age;
public:
//下面的成员和函数,都是公有
void setAge(int age);
void setName(char *name);
int getAge();
char * getName();
};
//
// Created by sunteng on 2023/9/5.
//
#include "Student.h"
// 根据 Student.h 头文件 ,写实现
// 和实现头文件那个函数,没有任何关系,相当于另外一个函数
/*void setAge(int age) {
}*/
void Student::setAge(int age) {
//this是一个Student类型的指针
this->age = age;
}
void Student::setName(char *name) {
this->name = name;
}
int Student::getAge() {
return this->age;
}
char * Student::getName() {
return this->name;
}
#include
#include "Student.h"//引入头文件即可
using namespace std;
int main() {
Student student1;//栈区开辟空间
//赋值
student1.setName("brett");
student1.setAge(19);
cout<<"name:"<<student1.getName()<<" ,age:"<<student1.getAge()<<endl;
Student* student2 = new Student();//new 堆区分配空间
student2->setAge(88);
student2->setName("mike");
cout<<"name:"<<student2->getName()<<" ,age:"<<student2->getAge()<<endl;
//堆区分配的对象使用完之后必须手动释放
if(student2){
delete student2;
student2 = NULL;
}
// new/delete 是一套 会调用构造函数 与 析构函数 【C++标准规范】
// malloc/free是一套 不调用构造函数 与 析构函数 【C的范畴,虽然不推荐,但是也是可以的】
return 0;
}
#include
#include
using namespace std;
class Student {
// 构造函数
public:
// 空参数构造函数
Student() {
cout << "空参数构造函数" << endl;
}
// 一个参数的构造函数
// :Student(name, 87) 等价 1.调用两个参数的构造函数, 2.再调用当前函数
// 学生说的:先调用两个参数的,再调用一个的
Student(char *name) :Student(name, 87) {
cout << "一个参数的构造函数" << endl;
if(!this->name){//防止重复赋值
this->name = name;
}
}
// 两个参数的构造函数
Student(char *name, int age) {
// this->name = name;
// 堆区
this->name = (char *) (malloc(sizeof(char *) * 10));
strcpy(this->name, name);
this->age = age;
cout << "两个参数的构造函数" << endl;
}
// 析构函数 Student对象的,临终遗言,Student对象被回收了,你做一些释放工作
// delete stu 的时候,我们的析构函数一定执行
// free不会执行析构函数,也意味着,你没法在析构函数里面,做释放工作, malloc也不会调用构造函数
~Student() {
cout << "析构函数" << endl;
// 必须释放 堆区开辟的成员
if (this->name) {
free(this->name);
this->name = NULL; // 执行NULL的地址,避免出现悬空指针
}
}
// 私有属性
private:
char *name;
int age;
// 公开的 set get 函数
public:
int getAge() {
return this->age;
}
char *getName() {
return this->name;
}
void setAge(int age) {
this->age = age;
}
void setName(char *name) {
this->name = name;
}
};
int main() {
// TODO =========== 下面是堆区 开辟空间的 堆区必须手动释放,否则内存占用越来
// 系统源码中,会看到,很多使用 new 关键字
// *stu ->:调用一级指针的成员
// new/delete
// C++中,必须使用 new/delete 一套
Student *stu = new Student("杜子腾");
cout << "name:" << stu->getName() << ", age:" << stu->getAge() << endl;
delete stu;
return 0;
}
// 4.拷贝构造函数。
#include
#include
using namespace std;
class Student {
public:
Student() { cout << "空参数构造函数" << endl; }
// 两个参数的构造函数
Student(char *name, int age) : name(name), age(age) {
cout << "两个参数构造函数" << endl;
}
// 析构函数
// ~Student(char * name) { } 这样写,就不是析构函数了,如果你这样写,C/C++编译器对你很无语
~Student() {
cout << "析构函数" << endl;
}
// 拷贝构造函数,它默认有,我们看不到,一旦我们写拷贝构造函数,会覆盖她
// 对象1 = 对象2
// 覆盖拷贝构造函数
Student(const Student & student) { // 常量引用:只读的,不让你修改
cout << "拷贝构造函数" << endl;
// 我们自己赋值
// 为什么要自己赋值,自己来控制,就可以 例如:-10
this->name = student.name;
this->age = student.age - 10;
cout << "自定义拷贝构造函数 内存地址 " << &student << endl;
}
// 私有属性
private:
char *name;
int age;
// 公开的 set get 函数
public:
int getAge() {
return this->age;
}
char *getName() {
return this->name;
}
void setAge(int age) {
this->age = age;
}
void setName(char *name) {
this->name = name;
}
};
struct Person {
int age;
char *name;
};
// TODO = 号的意义 隐士代码,引出 拷贝构造函数
int main() {
Person person1 = {100, "张三丰"};
// = 你看起来,没有什么特殊,隐士的代码:你看不到 C/C++编译器 会把p1的成员值赋值给p2成员
Person person2 = person1;
cout << &person1 << endl;
cout << &person2 << endl;
cout << person2.name << ", " << person2.age << endl;
// 思考:对象 对象1=对象2 默认的 拷贝构造函数
return 0;
}
// TODO 拷贝构造函数
/*int main() {
Student stu1("李鬼", 34);
Student stu2 = stu1;
cout << stu2.getName() << " , " << stu2.getAge() << endl;
cout << "main " << &stu1 << endl; // 地址的打印是一样的, 注意:cnetos的环境 地址打印有差异,要注意
// TODO 拷贝构造函数的注意点:
// Student stu1("李鬼", 34);
// Student stu2;
// stu2 = stu1; // 这样赋值是不会调用 自定义拷贝构造函数,但是会调用默认赋值
// Student stu2 = stu1; // 这样赋值是会调用 自定义拷贝构造函数,我们自己赋值
// cout << stu2.getName() << " , " << stu2.getAge() << endl;
getchar(); // 程序等待在这一行
return 0;
} // main函数弹,stu1栈成员会回收 stu2栈成员会回收
*/
// TODO 这种写法 拷贝构造函数 到底会不会调用
/*int main() {
Student *student1 = new Student("杜子腾", 39);
Student *student2 = student1; // 压根就不会执行拷贝构造函数(指针指向问题,和我们刚刚那个 对象2=对象1 是两回事)
student2->setAge(99);
cout << student1->getName() << student1->getAge() << endl;
return 0;
}*/
#define _CRT_SECURE_NO_WARNINGS // strcpy运行会报错,支持
#include
#include
using namespace std;
class Student
{
public:
int age;
char * name;
Student() { cout << "空参数构造函数" << endl; }
Student(char * name) :Student(name, 99) {
cout << "一个参数构造函数 this:" << this << endl;
}
Student(char * name, int age) {
cout << "二个参数构造函数 this:" << this << endl;
this->name = (char *)malloc(sizeof(char *)* 10);
strcpy(this->name, name);
this->age = age;
}
~Student() {
cout << "析构函数执行 &this->name:" << this->name << endl;
free(this->name);
this->name = NULL;
}
// 默认有一个拷贝构造函数 隐士的 我们看不见
// 一旦复写了拷贝构造函数,默认的还在吗? Java的构造函数一个思路
// 自定义拷贝构造函数 如果有堆成员,必须采用深拷贝
Student(const Student & stu) {
// stu 旧地址
// this 新地址
// s2 = 新地址
cout << "拷贝构造函数 &stu:" << &stu << " this:" << this << endl;//二者地址不一样
// 【浅拷贝】:新地址name 旧地址name 指向同一个空间,会造成,重复free的问题,引发奔溃
// 新地址name = 旧地址 (浅拷贝)
// this->name = stu.name;
// 【深拷贝】
this->name = (char *)malloc(sizeof(char *)* 10);
strcpy(this->name, name);
this->age = stu.age;
cout << "拷贝构造函数2 this->name:" << (this->name) << " stu.name:" << stu.name << endl;
// 深拷贝 后面见 原理全部打通的时候讲
} // 此拷贝构造函数执行完 旧会出现一个 this==新地址 给 main函数的 stu
// 默认的拷贝构造函数 是浅拷贝
};
void showStudent(Student stu) {
cout << "showStudent函数:" << &stu << " " << stu.name << "," << stu.age<< endl;
}
int main() {
Student stu("刘奋", 31);
showStudent(stu); // 弹栈后 新地址name释放一遍
showStudent(stu);
//
// showStudent(stu);
//
//
// showStudent(stu);
//
//
// showStudent(stu);
getchar();
return 0;
} // main函数弹栈 stu 旧地址
// 指针常量 常量指针 常量指针常量
#include
#include
#include
using namespace std;
int main() {
// *strcpy (char *__restrict, const char *__restrict);
// strcpy()
int number = 9;
int number2 = 8;
// 大道至简 一分钟搞定
// 常量指针
const int * numberP1 = &number;
// *numberP1 = 100; // 报错,不允许去修改【常量指针】存放地址所对应的值
// numberP1 = &number2; // OK,允许重新指向【常量指针】存放的地址
// 指针常量
int* const numberP2 = &number;
*numberP2 = 100; // OK,允许去修改【指针常量】存放地址所对应的值
// numberP2 = &number2; // 报错,不允许重新指向【指针常量】存放的地址
// 常量指针常量
const int * const numberP3 = &number;
// *numberP3 = 100; // 报错,不允许去修改【常量指针常量】存放地址所对应的值
// numberP3 = &number2; // 报错,不允许重新指向【常量指针常量】存放的地址
return 0;
}
// 2.类里运算符重载。
#include
using namespace std;
class Derry {
private:
int x,y;
public:
Derry() {
}
// 系统C++源码,大量使用此方式 :x(x), y(y)
Derry(int x, int y) :x(x), y(y) {}
// set get 函数
void setX(int x) {
this->x = x;
}
void setY(int y) {
this->y = y;
}
int getX() {
// this->x -9; 系统怕你在函数里面 修改了
return this->x;
}
int getY() {
return this->y;
}
// +号,运算符重载
/*Derry operator + (Derry derry1) {
// this指针 指向当前对象,所以只需要一个
int x = this->x + derry1.getX();
int y = this->y + derry1.getY();
return Derry(x, y);
}*/
// 系统是这样写的 常量引用:不允许修改,只读模式
// const 关键字的解释
// & 性能的提高,如果没有& 运行+ 构建新的副本,会浪费性能
// 如果增加了& 引用是给这块内存空间取一个别名而已
Derry operator + (const Derry& derry1) {
// this指针 指向当前对象,所以只需要一个
int x = this->x + derry1.x; // 我在类的里面,是可以拿私有成员的
int y = this->y + derry1.y; // 我在类的里面,是可以拿私有成员的
return Derry(x, y);
}
// 运算符- 重载
Derry operator - (const Derry & derry1) {
int x = this->x - derry1.x;
int y = this->y - derry1.y;
return Derry(x, y);
}
};
int main() {
Derry derry1(1000, 2000);
Derry derry2(3000, 4000);
// Derry result = derry1 + derry2;
Derry result = derry2 - derry1;
cout << result.getX() << " , " << result.getY() << endl;
return 0;
}
// 多继承 二义性2:
// 在真实开发过程中,严格避免出现 二义性
#include
using namespace std;
// 祖父类
class Object {
public:
int number;
};
// 父类1
class BaseActivity1 : public Object {
};
// 父类2
class BaseActivity2 : public Object {
};
// 子类
class Son : public BaseActivity1, public BaseActivity2 {
// 第二种解决方案: 在类中定义同名成员,覆盖掉父类的相关成员
public:
int number;
};
int main() {
Son son;
// error: request for member 'show' is ambiguous 二义性 歧义
// son.number = 2000;
// 第一种解决方案: :: 明确指定
son.BaseActivity1::number = 1000;
son.BaseActivity2::number = 1000;
// 第二种解决方案: 在类中定义同名成员,覆盖掉父类的相关成员
son.number = 3000;
// 第三种解决方案: 【虚基类】 属于 虚继承的范畴
return 0;
}
//第三种方式;虚基类
// 第三种解决方案: 【虚基类】 属于 虚继承的范畴
// 真实C++开始,是很少出现,二义性(歧义) 如果出现, 系统源码(系统用 第三种解决方案)
#include
using namespace std;
// 祖父类
class Object{
public:
int number;
void show() {
cout << "Object show run..." << endl;
}
};
// 父类1
class BaseActivity1 : virtual public Object {
};
// 父类2
class BaseActivity2 : virtual public Object {
};
// 子类
class Son : public BaseActivity1, public BaseActivity2 {
};
int main() {
Object object;
BaseActivity1 baseActivity1;
BaseActivity2 baseActivity2;
Son son;
object.number = 100;
baseActivity1.number = 200;
baseActivity2.number = 300;
son.number = 400;
object.show();
baseActivity1.show();
baseActivity2.show();
son.show();
cout << object.number << endl;
cout << baseActivity1.number << endl;
cout << baseActivity2.number << endl;
cout << son.number << endl;
return 0;
}
在C++中,虚基类是用于解决多继承中的菱形继承问题的一种机制。菱形继承指的是一个派生类同时继承自两个或多个基类,而这些基类又共同继承自同一个基类,形成了一个菱形的继承结构。
虚基类的作用是解决菱形继承带来的二义性和冗余的问题。当一个派生类通过多条路径继承自同一个基类时,如果不使用虚基类,那么在派生类中就会存在多个基类子对象的实例,这样就会导致同名成员在派生类中出现冗余,访问这些成员时会产生二义性。
通过使用虚基类,可以确保在派生类中只有一个基类子对象的实例,从而避免了冗余和二义性。虚基类的成员在派生类中只有一份拷贝,这样就可以确保派生类对基类成员的访问是唯一的。
使用虚基类的语法是在派生类的基类列表中,将虚基类声明为虚基类。例如:
class Base {
public:
// Base类的成员
};
class Derived1 : virtual public Base {
public:
// Derived1类的成员
};
class Derived2 : virtual public Base {
public:
// Derived2类的成员
};
class Derived3 : public Derived1, public Derived2 {
public:
// Derived3类的成员
};
在上面的例子中,Base是虚基类,Derived1和Derived2都通过虚继承方式继承自Base。Derived3通过多继承同时继承自Derived1和Derived2,这样就避免了菱形继承带来的问题。
总结来说,虚基类的作用是解决多继承中的菱形继承问题,避免冗余和二义性,确保派生类对基类成员的访问是唯一的。
#include
using namespace std;
class Person {
public:
char *name;
int age;
public:
Person(char *name, int age) : name(name) {
this->age = age;
cout << "Person 构造函数" << endl;
}
void print() {
cout << this->name << " , " << this->age << endl;
}
};
// 1.默认是 隐式代码: : private Person
// 2.私有继承:在子类里面是可以访问父类的成员,但是在类的外面不行
// 3.必须公开继承,才可以访问父类的成员
class Student : public Person {
// 类 默认是私有,注意下
private:
char * course;
public:
// :父类 , 给自己子类成员初始化
Student(char * name, int age, char* course) : Person(name, age) , course(course) {
cout << "Student 构造函数" << endl;
}
void test() {
cout << name << endl;
cout << age << endl;
print();
}
};
int main() {
Student stu("李元霸", 99, "C++");
// 公开继承,才可以拿父类的成员
stu.name = "李四";
return 0;
}
// 2.C++static关键字。 正确的写法
/**
* 静态的总结:
* 1.可以直接通过类名::静态成员(字段/函数)
* 2.静态的属性必须要初始化,然后再实现(规则)
* 3.静态的函数只能取操作静态的属性和方法
*/
#include
using namespace std;
class Dog {
public:
char * info;
int age;
// 先声明
static int id;
static void update() {
id += 100;
// 报错:静态函数不能调用非静态函数
// update2();
}
void update2() {
id = 13;
}
};
// 再实现
int Dog::id = 9;
int main() {
Dog dog;
dog.update2(); // 普通函数
Dog::update(); // 静态函数
dog.update(); // 对象名.静态函数(一般都是使用::调用静态成员,这种方式可以 知道就行)
cout << Dog::id << endl;
return 0;
}
在C++中,友元类和友元函数是一种特殊的访问权限控制机制,允许一个类或函数访问另一个类的私有成员。
友元类(Friend Class)是指在一个类中声明另一个类为友元,从而使得被声明的类可以访问声明它为友元的类的私有成员。友元类的声明通常出现在类的定义中,例如:
class A {
friend class B; // B是A的友元类
private:
int privateData;
};
class B {
public:
void accessPrivateData(A& obj) {
int data = obj.privateData; // 可以访问A类的私有成员
}
};
在上面的例子中,类B被声明为类A的友元类,因此类B可以访问类A的私有成员privateData。
友元函数(Friend Function)是指在一个类中声明一个函数为友元,从而使得被声明的函数可以访问声明它为友元的类的私有成员。友元函数的声明通常出现在类的定义中,例如:
class A {
friend void printPrivateData(A& obj); // printPrivateData是A的友元函数
private:
int privateData;
};
void printPrivateData(A& obj) {
int data = obj.privateData; // 可以访问A类的私有成员
}
在上面的例子中,函数printPrivateData被声明为类A的友元函数,因此函数printPrivateData可以访问类A的私有成员privateData。
需要注意的是,友元类和友元函数破坏了封装性,因此应该谨慎使用。过度使用友元可能会导致代码的可维护性和可读性下降。在设计类的时候,应该优先考虑使用成员函数和访问修饰符(如public、private、protected)来实现对类成员的访问控制。只有在确实需要访问私有成员的特殊情况下,才考虑使用友元类或友元函数。
// 友元函数
// 老外:你是它的好朋友,那就可以拿私有成员给好朋友
#include
using namespace std;
class Person {
private: // 私有的age,外界不能访问
int age = 0;
public:
Person(int age) {
this->age = age;
}
int getAge() {
return this->age;
}
// 定义友元函数 (声明,没有实现)
friend void updateAge(Person * person, int age);
};
// 友元函数的实现,可以访问所以私有成员
void updateAge(Person* person, int age) {
// 默认情况下:不能修改 私有的age
// 谁有这个权限:友元(拿到所有私有成员)
person->age = age;
}
int main() {
Person person = Person(9);
updateAge(&person, 88);
cout << person.getAge() << endl;
return 0;
}
#include
#include // 可变参数的支持
using namespace std;
// Java的可变参数: int ...
// C++的可变参数写法:...
// count变量的第二个用处,用于循环遍历长度
void sum(int count, ...) {
va_list vp; // 可变参数的动作
// 参数一:可变参数开始的动作vp
// 参数二:内部需要一个 存储地址用的参考值,如果没有第二个参数,内部他无法处理存放参数信息
va_start(vp, count);
// 到这里后:vp就已经有丰富的信息
for (int i = 0; i < count; ++i) {
int r = va_arg(vp, int);
cout << r << endl;
}
// 越界 系统值 乱码
// 取出可变参数的一个值 【娶不到后,会取系统值 乱码】
// int number = va_arg(vp, int);
// cout << number << endl;
// 关闭阶段(规范:例如:file文件一样 要关闭)
va_end(vp);
}
// 1.可变参数
int main() {
sum(3, 6,7,8); // 真实开发过程的写法
return 0;
}
class Worker {
public:
char * name;
int age = NULL; // C++中不像Java,Java有默认值, 如果你不给默认值,那么就是系统值 -64664
// int * const 指针常量 指针常量【地址对应的值能改,地址不可以修改】
// const int * 常量指针 常量指针【地址可以修改,地址对应的值不能改】
// 纠结:原理:为什么可以修改age
// 默认持有隐士的this【类型 * const this】
// 类型 * const 指针常量:代表指针地址不能被修改,但是指针地址的值是可以修改的
void change1() {
// 代表指针地址不能被修改
// this = 0x6546; // 编译不通过,地址不能被修改,因为是指针常量
// 地址不可以修改
// this = 0x43563;
// 隐士的this
// 但是指针地址的值是可以修改的
// 地址对应的值能改
this->age = 100;
this->name = "JJJ";
}
// 默认现在:this 等价于 const Student * const 常量指针常量(地址不能改,地址对应的值不能改)
void changeAction() const {
// 地址不能改
// this = 0x43563;
// 地址对应的值不能改
// this->age = 100;
}
// 原理:修改隐士代码 const 类型 * const 常量指针常量
void showInfo() const {
// this->name = "";
// this->age = 88;
// 只读的
cout << "age:" << age << endl;
}
};
C++ 11 之后添加了新的标准线程库 std::thread,std::thread 在 头文件中声明,因此使用 std::thread 时需要包含 在 头文件。
// 演示多线程的CPP程序
// 使用三个不同的可调用对象
#include
#include
using namespace std;
// 一个虚拟函数
void foo(int Z)
{
for (int i = 0; i < Z; i++) {
cout << "线程使用函数指针作为可调用参数\n";
}
}
// 可调用对象
class thread_obj {
public:
void operator()(int x)
{
for (int i = 0; i < x; i++)
cout << "线程使用函数对象作为可调用参数\n";
}
};
int main()
{
cout << "线程 1 、2 、3 "
"独立运行" << endl;
// 函数指针
thread th1(foo, 3);//不需要显式调用start()方法来启动线程,std::thread对象的构造函数会自动启动线程的执行。
// 函数对象
thread th2(thread_obj(), 3);
// 定义 Lambda 表达式
auto f = [](int x) {
for (int i = 0; i < x; i++)
cout << "线程使用 lambda 表达式作为可调用参数\n";
};
// 线程通过使用 lambda 表达式作为可调用的参数
thread th3(f, 3);
// 等待线程完成
// 等待线程 t1 完成
th1.join();
// 等待线程 t2 完成
th2.join();
// 等待线程 t3 完成
th3.join();
return 0;
}
/*
线程 1 、2 、3 独立运行
线程使用函数指针作为可调用参数
线程使用函数指针作为可调用参数
线程使用函数指针作为可调用参数
线程使用函数对象作为可调用参数
线程使用函数对象作为可调用参数
线程使用函数对象作为可调用参数
线程使用 lambda 表达式作为可调用参数
线程使用 lambda 表达式作为可调用参数
线程使用 lambda 表达式作为可调用参数
*/