C++简介:C++是C的拓展,早期叫做“带类的C”,它增加了很多新的语法,目的是提高开发效率。因此学了C语言就相当于学了 C++ 的一半,从C语言转向 C++ 时,不需要再从头开始,接着C语言往下学就可以。
C++与C的设计差异
C++加入了命名空间(类似于JAVA包体系),命名空间的出现是为了更好的组织代码
C++加入了class OOP概念,为业务服务,而C所有东西都要基于内存处理
::域访问操作符
-> 指针的指向运算符,通常用在结构体中
& 取别名(C++的引用就是别名,引用在本质上属于指针)
命名空间类似于Java中的Package包的概念,类似于 import package;
其中::访问修饰符
#include
int main()
{
std::cout << "Hello World!\n";//Hello World!
}
使用命名空间可以省略std::
#include
//标准命名空间standard(包含很多标准的定义)
using namespace std;
int main()
{
cout << "Hello World!\n";//Hello World!
}
自定义命名空间与命名空间嵌套
//自定义命名空间
namespace NSP_A {
int a = 9;
struct Teacher {
char name[20];
int age;
};
struct Student {
char name[20];
int age;
};
}
namespace NSP_B {
int a = 12;
//命名空间嵌套
namespace NSP_C {
int c = 90;
}
}
int main()
{
std::cout << NSP_A::a << std::endl;//9
std::cout << NSP_B::NSP_C::c << std::endl;//90
}
使用命名空间结构体的两种写法
//使用命名空间中的结构体
NSP_A::Student s;
// using NSP_A::Student;
// Student s;
s.age = 19;
C++创建对象分配与java不同
Java声明对象不会开辟内存,在new的时候才会开辟内存。
C++在声明对象就开辟内存了,如果是指针则开辟指针空间大小,如果是类对象则直接开辟类对象所需大小,详细参考
#include
using namespace std;
#define PI 3.14
class MyCircle {
private:
double r;
double s;
public:
void setR(double r) {
this->r = r;
}
//获取面积
double getS() {
return PI * r * r;
}
};
int main() {
MyCircle c1;
c1.setR(4);
cout << "圆的面积:" << c1.getS() << endl;
return 0;
}
也可以先定义,再通过域访问操作符实现函数体
class MyCircle {
private:
double r;
double s;
public:
void setR(double r) {
this->r = r;
}
//获取面积
double getS();
};
//也可以先定义再实现
double MyCircle::getS()
{
return PI * r * r;
}
区别 | 声明方式 | 大小 | 访问方式 |
---|---|---|---|
类对象 | book bb; | 内容的大小 | .(点)运算符 |
类指针 | book *b; | 固定的函数指针大小8 | ->指针箭头函数操作 |
class book {
public:
void set_name(string n) {
this->name = n;
}
void print_book() {
cout << "This book's name is " << this->name << endl;
}
private:
string name;
};
int main() {
// 1、类对象,后面的 = *new book()可省略
// book bb = *new book();
book bb;
cout << new book() << endl;//有内容
cout << sizeof bb << endl;//打印:24,打印出来是内容大小
//.(点)运算符
bb.set_name("zhangsan");
bb.print_book();//T打印:his book's name is zhangsan
// 2、类指针
book *b;
cout << sizeof b << endl;//打印:8,打印出来是地址大小
//->指针操作
b->set_name("zhangsan");
b->print_book();//打印:This book's name is zhangsan
}
在c++中结构体没有类的解构、析构等方法,因此没有类方便
#include
using namespace std;
struct MyTeacher {
public:
char name[20];
int age;
public:
void say() {
cout << this->age << "岁" << endl;
}
};
int main() {
MyTeacher t1;
t1.age = 10;
t1.say();//10岁
return 0;
}
布尔类型:为false的情况:0、false、NULL
大小:占一个字节
#include
using namespace std;
int main() {
//bool isSingle = true;
bool isSingle = 17;
if (isSingle) {
cout << "单身" << endl;//单身
cout << sizeof(bool) << endl;//1
}
else {
cout << "有对象" << endl;
}
int a = 10, b = 20;
((a > b) ? a : b) = 30;
cout << b << endl;//30
return 0;
}
别名的声明方式int& b = a,别名可以有多个
#include
using namespace std;
int main() {
//变量名-门牌号(内存空间0x00001的别名,可不可以有多个名字?)
int a = 9;
//b就这个内存空间另外一个别名
//& C++中的引用
int& b = a;
cout << b << endl;
return 0;
}
//指针值交换
void swap_1(int *a,int *b){
int tmp=*a;;
*a=*b;
*b=tmp;
}
//引用值交换
void swap_2(int& a,int& b){
int tmp=a;
a=b;
b=tmp;
}
int main(){
int x=10;
int y=20;
printf("%d,%d\n",x,y);//10,20
swap_1(&x, &y);
// swap_2(x, y);
printf("%d,%d\n",x,y);//20,10
return 0;
}
struct Teacher{
char *name;
int age;
};
void myprint(Teacher& t){
cout<name<<","<age<
struct Teacher{
char* name;
int age;
};
void myprint(Teacher& t){
cout<name<<","<age<
//二级指针
void getTeacher(Teacher **p){
Teacher* tmp=(Teacher*)malloc(sizeof(Teacher));
tmp->age=20;
*p=tmp;
}
//别名
void getTeacher(Teacher* &p){
p=(Teacher*)malloc(sizeof(Teacher));
p->age=20;
}
int main(){
Teacher* t=NULL;
getTeacher(&t);
// getTeacher(t);
cout<age<
int main() {
int a = 2, b = 3;
//指针常量,指针的常量,不改变地址的指针,但是可以修改它指向的内容
int *const p1 = &a;
//p1=&b;//不允许Cannot assign to variable 'p1' with const-qualified type 'int *con
*p1 = 4;//允许
//常量指针,指向常量的指针,内容不能修改
const int *p2 = &a;
p2 = &b;//允许
// *p2=4;//不允许Read-only variable is not assignable
//会锁定内容
cout << *p2 << endl;//3
a=33;
cout << *p2 << endl;//3
return 0;
}
函数参数指定为常量指针
void myprint(const int& a){
cout<
int main(){
// 引用必须要有值,不能为空
// int& a=NULL;//不允许Non-const lvalue reference to type 'int' cannot bind to a temporary of type 'long'
//常引用
//常引用类似于java中final
int a=10,b=9;
const int& c=a;
// c=b;//不允许Cannot assign to variable 'c' with const-qualified type 'const int &'
//字面量
const int& d=70;
return 0;
}
struct Teacher{
char name[20];//20
int age;//4
char* address;//一个字符占两个字节4*2=8
};
int main(){
Teacher t;
Teacher& t1=t;
Teacher* p=&t;
cout<
指针方式判空:编译通过,不能提前防止错误
别名方式判空:编译不通过,提前防止错误
struct Teacher{
char name[20];//20
int age;//4
};
void myprint(Teacher* t){
cout<name<<","<age<
void myprint(int x,int y=9,int z=8){
cout<
可变参数需要约定参数类型
void VariableArgumentMethod(int pArg,...){
//声明一个指针, 用于持有可变参数
va_list args_p;
//将 pArg 初始化为指向第一个参数
va_start(args_p, pArg);
// 输出参数
for (int i=0; i!=pArg; i++) {
// 获取 pArg 所指向的参数并输出
printf("%d, ", va_arg(args_p, int) );
}
//结束
va_end(args_p);
}
int main(){
VariableArgumentMethod(3,20,30,40);//20, 30, 40
return 0;
}
实体类(头文件.h、执行文件.cpp)、main函数分离写法
//MyTeacher.h
class MyTeacher {
public:
int age;
char *name;
public:
void setAge(int age);
int getAge();
void setName(char *name);
char *getName();
};
//MyTeacher.cpp
#include
#include "MyTeacher.h"
void MyTeacher::setAge(int age) {
this->age = age;
}
int MyTeacher::getAge() {
return this->age;
}
void MyTeacher::setName(char *name) {
this->name = name;
}
char *MyTeacher::getName() {
return this->name;
}
//main.cpp
#include
#include "MyTeacher.h"
using namespace std;
int main() {
MyTeacher t1;
t1.setName("zhangsan");
t1.setAge(18);
cout<
using namespace std;
class Teacher {
private:
char *name;
int age;
public:
//无参构造函数(写了,就会覆盖默认的无参构造函数)
Teacher() {
cout << "无参构造函数" << endl;
}
Teacher(char *name, int age) {
this->name = name;
this->age = age;
cout << "有参构造函数" << endl;
}
char *getName() {
return name;
}
};
int main() {
Teacher t1;
Teacher t2("Jsason", 20);
//另外一种调用方式
Teacher t3 = Teacher("Jack", 21);
return 0;
}
using namespace std;
class Teacher {
private:
char *name;
public:
//无参构造函数(写了,就会覆盖默认的无参构造函数)
Teacher() {
this->name=(char*)malloc(100);
strcpy(name,"zhangsan");
cout << "无参构造函数" << endl;
}
//析构函数
//当对象要被系统释放时,析构函数被调用
//作用:善后处理
~Teacher(){
cout<<"析构"<name);
}
};
int main() {
Teacher t1;
return 0;
}
//无参构造函数
//析构
class Teacher{
private:
char* name;
int age;
public:
Teacher(char* name,int age){
this->name=name;
this->age=age;
cout<<"有参构造函数"<name=obj.name;
this->age=obj.age;
cout<<"拷贝构造函数"<
声明时赋值
int main(){
Teacher t1("rose",20);
Teacher t2=t1;
return 0;
}
//有参构造函数
//拷贝构造函数
作为参数传入,实参给形参赋值
void func1(Teacher t){
t.myprint();
}
int main(){
Teacher t1("rose",20);
func1(t1);
return 0;
}
//有参构造函数
//拷贝构造函数
//rose,20
作为函数返回值返回,给变量初始化赋值
第一次拷贝构造:t1赋值给形参赋值
第二次拷贝构造:func1返回t对象
Teacher func1(Teacher t){
t.myprint();
return t;
}
int main(){
Teacher t1("rose",20);
Teacher t3 = func1(t1);
return 0;
}
//有参构造函数
//拷贝构造函数
//rose,20
//拷贝构造函数
浅拷贝也就是赋值拷贝,编译器默认实现浅拷贝。
下面代码name字段为堆内存地址,直接赋值实现的是浅拷贝。
class Teacher{
private:
char* name;
int age;
public:
Teacher(char* name,int age){
this->name=(char*)malloc(100);
strcpy(this->name,name);
this->age=age;
cout<<"有参构造函数"<name);
this->name = NULL;
}
//浅拷贝
// Teacher(const Teacher& obj) {
// this->name = obj.name;
// this->age = obj.age;
// }
void myprint(){
cout<
深拷贝。对name字段重新开辟空间赋值
class Teacher{
private:
char* name;
int age;
public:
Teacher(char* name,int age){
this->name=(char*)malloc(100);
strcpy(this->name,name);
this->age=age;
cout<<"有参构造函数"<name);
this->name = NULL;
}
//深拷贝
Teacher(const Teacher& obj) {
//复制name属性
int len = strlen(obj.name);
this->name = (char*)malloc(len+1);
strcpy(this->name, obj.name);
this->age = obj.age;
}
void myprint(){
cout<
这里c++初始化属性对象使用这个极大方便了初始化
Student(int id,char t1_n,char t2_n):t1(t1_n),t2(t2_n)
class Teacher{
private:
char* name;
public:
Teacher(char* name){
this->name=name;
cout<<"Teacher有参构造函数"<name;
}
};
class Student{
private:
int id;
//属性对象
Teacher t1;
Teacher t2;
public:
Student(int id,char *t1_n,char* t2_n):t1(t1_n),t2(t2_n){
this->id=id;
cout<<"Student有参构造函数"<
类的创建与释放
class Teacher{
private:
char* name;
public:
Teacher(char* name){
this->name=name;
cout<<"Teacher有参构造函数"<name=name;
}
char* getName(){
return this->name;
}
};
int main(){
//C++ 动态内存分配
//会调用构造和析构函数
Teacher *t1=new Teacher("Jack");
cout<getName()<setName("Rose");
cout<getName()<
数组的创建与释放
int main(){
//数组类型
//C
int *p1=(int*)malloc(sizeof(int)*10);
p1[0]=9;
free(p1);
//C++
int *p2=new int[10];
p2[0]=9;
delete p2;
return 0;
}
class Teacher{
public:
char* name;
//计数器
static int total;
public:
Teacher(char* name){
this->name=name;
cout<<"Teacher有参构造函数"<name=name;
}
char* getName(){
return this->name;
}
//计数,静态函数
static void count(){
total++;
cout<
C/C++ 内存分区:栈、堆、全局(静态、全局)、常量区(字符串)、程序代码区
普通属性与结构体相同的内存布局
这里类A、类B不计算静态变量、方法的大小
class A{
public:
int i;
int j;
int k;
static int m;
};
class B{
public:
int i;
int j;
int k;
void myprint(){
cout<<"打印"<
常函数中只有只读操作
class Teacher{
private:
char* name;
int age;
public:
Teacher(char* name,int age){
this->name=name;
this->age=age;
cout<<"Teacher有参构造函数"<name="Jack";
//不允许,改变this指针的值
//this = (Teacher*)0x00009;
cout << this->name << "," << this->age << endl;
}
void myprint2(){
cout << this->name << "," << this->age << endl;
}
};
int main(){
Teacher t1("Jack",20);
//常量对象只能调用常量函数,不能调用非常量函数
const Teacher t2("Rose",18);
//常函数,当前对象不能被修改,防止数据成员被非法访问
//t2.myprint2();
return 0;
}
友元函数的实现,在友元函数中可以访问私有的属性
java中的反射访问私有属性,底层实现也是友元函数
class A{
//友元函数
friend void modify_i(A* p,int a);
private:
int i;
public:
A(int i){
this->i=i;
}
void myprint(){
cout<i=a;
}
int main(){
A* a=new A(10);
a->myprint();//10
modify_i(a, 20);
a->myprint();//20
return 0;
}
class A{
//友元类
friend class B;
private:
int i;
public:
A(int i){
this->i=i;
}
void myprint(){
cout<
class Point{
public:
int x;
int y;
public:
Point(int x=0,int y=0){
this->x=x;
this->y=y;
}
void myprint(){
cout<
或者写在成员函数中
class Point{
public:
int x;
int y;
public:
Point(int x=0,int y=0){
this->x=x;
this->y=y;
}
//成员函数,运算符重载
Point operator+(Point& p1){
Point tmp(this->x+p1.x,this->y+p1.y);
return tmp;
}
void myprint(){
cout<
当属性私有时,通过友元函数完成运算符重载
class Point{
friend Point operator+(Point& p1,Point& p2);
friend Point operator-(Point& p1,Point& p2);
private:
int x;
int y;
public:
Point(int x=0,int y=0){
this->x=x;
this->y=y;
}
void myprint(){
cout<
#include
using namespace std;
class Human{
public:
void say(){
cout<<"说话"<
class Human{
public:
Human(char* name,int age){
this->name=name;
this->age=age;
}
void say(){
cout<<"说话"<brother=brother;
}
void chasing(){
cout<<"泡妞"<
#include
using namespace std;
class Human{
public:
Human(char* name,int age){
this->name=name;
this->age=age;
cout<<"Human 构造函数"<brother=brother;
cout<<"Man 构造函数"<
m1.Human::say();
#include
using namespace std;
class Human{
public:
Human(char* name,int age){
this->name=name;
this->age=age;
}
void say(){
cout<<"说话"<brother=brother;
}
void chasing(){
cout<<"泡妞"<
//人
class Person{
};
//公民
class Citizen{
};
//学生,既是人,又是公民
class Student:public Person,public Citizen{
};
//继承的访问修饰
//基类中 继承方式 子类中
//public & public继承 = > public
//public & protected继承 = > protected
//public & private继承 = > private
//
//protected & public继承 = > protected
//protected & protected继承 = > protected
//protected & private继承 = > private
//
//private & public继承 = > 子类无权访问
//private & protected继承 = > 子类无权访问
//private & private继承 = > 子类无权访问
继承的二义性:多继承中对父类都存在的重复属性赋值会在编译中报错。
1,二义性可以指定父类使用如b.A1::name
2,打印时继承的二义性默认使用最后一个父类的属性
3,多继承可以使用virtual虚继承处理二义性问题
class A{
public:
char* name;
};
class A1:virtual public A{
public:
A1(){
this->name="A1";
}
};
class A2:virtual public A{
public:
A2(){
this->name="A2";
}
};
class B:public A1,public A2{
};
int main(){
B b;
// b.name="Jack";
cout<
虚函数运用在多态上,可以提高程序的拓展性。
多态分为:
1,动态多态:程序运行过程中,觉得哪一个函数被调用(重写)
2,静态多态:重载
java的多态条件
1、继承
2、重写父类方法
3、父类引用持有子类对象
以下示例通过动态多态体现了虚函数
//main函数
#include "Plane.h"
#include "Jet.h"
#include "Copter.h"
void bizPlay(Plane &p){
p.fly();
p.land();
}
int main(){
Copter p2;
bizPlay(p2);
Jet p3;
bizPlay(p3);
return 0;
}
//直升飞机在原地起飞...
//直升飞机降落在女神的屋顶...
//喷气式飞机在跑道上起飞...
//喷气式飞机在跑道上降落...
普通飞机类
//Plane.h文件
//普通飞机
class Plane{
public:
virtual void fly();
virtual void land();
};
//Plane.cpp文件
#include "Plane.h"
#include
using namespace std;
void Plane::fly(){
cout<<"起飞"<
直升飞机类
//Copter.h文件
#pragma once
#include
#include "Plane.h"
class Copter:public Plane{
public:
virtual void fly();
virtual void land();
};
//Copter.cpp文件
#include "Copter.h"
#include
using namespace std;
void Copter::fly() {
cout << "直升飞机在原地起飞..." << endl;
}
void Copter::land() {
cout << "直升飞机降落在女神的屋顶..." << endl;
}
喷气式飞机类
//Jet.h文件
#include "Plane.h"
//喷气式飞机
class Jet:public Plane{
public:
virtual void fly();
virtual void land();
};
//Jet.cpp文件
void Jet::fly(){
cout << "喷气式飞机在跑道上起飞..." << endl;
}
void Jet::land() {
cout << "喷气式飞机在跑道上降落..." << endl;
}
纯虚函数(抽象类)
1.当一个类具有一个纯虚函数,这个类就是抽象类
2.抽象类不能实例化对象
3.子类继承抽象类,必须要实现纯虚函数,如果没有,子类也是抽象类
抽象类的作用:为了继承约束,根本不知道未来的实现
class Shape{
public:
virtual void sayArea()=0;
void print(){
cout<<"hi"<r=r;
}
void sayArea(){
cout<<"圆的面积"<<(3.14*r*r)<
//接口(只是逻辑上的划分,语法上跟抽象类的写法没有区别)
//可以当做一个接口
class Drawable{
virtual void draw();
};
void myswap(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
void myswap(char &a,char &b){
char temp=a;
a=b;
b=temp;
}
int main(){
int a=10,b=20;
myswap(a, b);
char c='c',d='d';
myswap(c, d);
return 0;
}
发现:这两个函数业务逻辑一样,数据类型不一样
template
void myswap(T& a,T& b){
T temp=a;
a=b;
b=temp;
}
类似于java的List list;
template
class A{
public:
A(T a){
this->a=a;
}
protected:
T a;
};
//普通类继承模板类
class B:public A{
B(int a,int b):A(a){
this->b=b;
}
private:
int b;
};
//模板类继承模板类
template
class C:public A{
public:
C(TT c,TT a):A(a){
this->c=c;
}
protected:
TT c;
};
int main(){
//实例化模板类对象
//类似于java的List list;
A a(6);
return 0;
}
类为空情况下:
在C++中分配体系中什么没有sizeof为1
在C中什么都没有默认分配0
class Base {
};
int main()
{
cout << "Base Size:"<< sizeof(Base) <
类里有一个虚函数情况下:
这里8+4=12,字节对齐为16
字节对齐(内存对齐):结构体变量的大小,必须是最宽基本数据类型的整数倍。
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
int base;
};
int main()
{
cout << "Base Size:"<< sizeof(Base) <
类里有多个虚函数情况下:
在C++中用虚函数表存放虚函数,这里只有一个虚函数指针,因此仍然为8+4=12,字节对齐为16
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
int main()
{
cout << "Base Size:"<< sizeof(Base) <
有虚函数继承无重载情况下:
这里子类Child1会拷贝一份Base的虚函数表到子类虚函数表中,因此8+4+4=16,字节对齐后仍然是16
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
//子类1,无虚函数重载
class Child1 : public Base
{
public:
virtual void f1() { cout << "Child1::f1" << endl; }
virtual void g1() { cout << "Child1::g1" << endl; }
virtual void h1() { cout << "Child1::h1" << endl; }
int child1;
};
int main()
{
cout << "Child1 Size:"<< sizeof(Child1) <
有一个虚函数重载情况下子类虚函数表会优先存放自己的虚函数,因此这里的虚函数f会覆盖父类的。sizeof大小仍然是8+4+4=16
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
//子类2,有一个虚函数重载
class Child2 : public Base {
public:
virtual void f() { cout << "Child2::f" << endl; }
virtual void g1() { cout << "Child2::g1" << endl; }
virtual void h1() { cout << "Child2::h1" << endl; }
int child2;
};
int main() {
cout << "Child2 Size:" << sizeof(Child2) << endl;//Child2 Size:16
}
全部虚函数重载情况下回完全覆盖父类的虚函数表
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
//子类3,全部虚函数重载
class Child3 : public Base
{
public:
virtual void f() { cout << "Child3::f" << endl; }
virtual void g() { cout << "Child3::g" << endl; }
virtual void h() { cout << "Child3::h" << endl; }
protected:
int x;
};
int main()
{
cout << "Child3 Size:"<< sizeof(Child3) <
多继承情况下,第一个继承类的虚函数会拷贝到子类的虚函数表中,第二个的继承类会把指针给拷贝过来多生成一个虚函数表。也就是说有几个继承就有几个虚表
因此这里的sizeof为两个虚函数表8,三个int变量4,8+8+4+4+4,字节对齐为24
class Base3 {
public:
virtual void f3() { cout << "Base3::f3" << endl; }
virtual void g3() { cout << "Base3::g3" << endl; }
virtual void h3() { cout << "Base3::h3" << endl; }
int base3;
protected:
private:
};
class Base4 {
public:
virtual void f4() { cout << "Base4::f4" << endl; }
virtual void f3() { cout << "Base4::f4" << endl; }
virtual void g4() { cout << "Base4::g4" << endl; }
virtual void h4() { cout << "Base4::h4" << endl; }
int base4;
protected:
private:
};
class Child5 : public Base3, public Base4 {
public:
virtual void f3() { cout << "child5::f3" << endl; }
virtual void f4() { cout << "Child5::f4" << endl; }
virtual void f5() { cout << "Child5::f5" << endl; }
virtual void g5() { cout << "Child5::g5" << endl; }
virtual void h5() { cout << "Child5::h5" << endl; }
int child5;
protected:
private:
};
int main() {
cout << "Child5 Size:" << sizeof(Child5) << endl;//Child5 Size:32
}
棱形继承在多继承的基础上添加了父类,其原理都一样。这里sizeof为(8+4+4)+(8+4+4+4)字节对齐为40
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
//多重继承体系下的
class Base3 : public Base {
public:
virtual void f3() { cout << "Base3::f3" << endl; }
virtual void g3() { cout << "Base3::g3" << endl; }
virtual void h3() { cout << "Base3::h3" << endl; }
int base3;
};
class Base4 : public Base {
public:
virtual void f4() { cout << "Base4::f4" << endl; }
virtual void f3() { cout << "Base4::f4" << endl; }
virtual void g4() { cout << "Base4::g4" << endl; }
virtual void h4() { cout << "Base4::h4" << endl; }
int base4;
};
class Child6 : public Base3, public Base4 {
public:
int child5;
};
int main() {
cout << "Child6 Size:" << sizeof(Child6) << endl;//Child6 Size:40
}
虚拟继承的子类的内存结构,和普通继承完全不同。虚拟继承的子类,有单独的虚函数表,另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是自己的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。
如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换
如下面虚继承示例中,sizeof便为(8+4)+(8+4)内存对齐也就是16+16=32
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
};
class Child : virtual public Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void f1() { cout << "Child::f1" << endl; }
virtual void g1() { cout << "Child::g1" << endl; }
virtual void h1() { cout << "Child::h1" << endl; }
int child;
};
int main() {
cout << "Child6 Size:" << sizeof(Child) << endl;//Child6 Size:32
return 0;
}
1.当前类有虚, 则开辟虚表,虚表指针给入当前对象指针
2.多继承下,开辟虚表根据继承的类走,会有多个指针
下面示例Child7的结构为:Base3、Base4虚继承于Base,然后Child7多继承于Base3、Base4,Child7的虚表会和Base3合并。
因此sizeof则为:(8+4)+(8+4)+(4+8+4)字节对齐后为16+16+16=48
class Base
{
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int base;
protected:
private:
};
class Base3 :virtual public Base
{
public:
virtual void f3() { cout << "Base3::f3" << endl; }
virtual void g3() { cout << "Base3::g3" << endl; }
virtual void h3() { cout << "Base3::h3" << endl; }
int base3;
protected:
private:
};
class Base4 :virtual public Base
{
public:
virtual void f4() { cout << "Base4::f4" << endl; }
virtual void g4() { cout << "Base4::g4" << endl; }
virtual void h4() { cout << "Base4::h4" << endl; }
int base4;
protected:
private:
};
class Child7 : public Base3, public Base4
{
public:
virtual void f() { cout << "child7::f" << endl; }
virtual void f3() { cout << "child7::f3" << endl; }
virtual void f4() { cout << "Child7::f4" << endl; }
virtual void f7() { cout << "Child7::f7" << endl; }
virtual void g7() { cout << "Child7::g7" << endl; }
virtual void h7() { cout << "Child7::h7" << endl; }
int child7;
protected:
private:
};
int main()
{
cout << "Child7 Size:" << sizeof(Child7) << endl;//Child7 Size:48
return 0;
}
Linux下32位环境的用户空间内存分布情况
其中栈内存是递减开辟,堆内存是递增开辟。
那么我们依次开辟class book对象,发现内存地址是递减的,说明类的内存开辟在栈上
class book {
};
int main() {
book b1 = *new book();
book b2 = *new book();
book b3 = *new book();
book b4 = *new book();
cout << &b1 << " | " << &b2 << " | " << &b3 << " | " << &b4 << " | " << endl;
return 0;
}
//0x7ff7ba3a2ff8 | 0x7ff7ba3a2ff0 | 0x7ff7ba3a2fe8 | 0x7ff7ba3a2fe0 |
int main(){
try {
int age = 50;
if (age > 200){
throw 9.8;
}
else if (age < 100) {
char *a="抛一个字符串";
throw a;
}
}
catch (int a) {
cout << "int 异常" << a << endl;
}
catch (char* b) {
cout << b << endl;
}
catch (...) {
cout << "未知异常" << endl;
}
return 0;
}
void mydiv(int a,int b){
if (b==0) {
char *a="除数为0";
throw a;
}
}
void func(){
try {
mydiv(0, 0);
} catch (char *a) {
throw a;
}
}
int main(){
try {
func();
} catch (char* a) {
cout<
抛出对象的引用【最合适】。
引用是对象的别名
class MyException{};
void mydiv(int a,int b){
if (b==0) {
throw MyException();
}
}
int main(){
try {
mydiv(8, 0);
}
catch (MyException& e) {
cout<<"MyException引用"<
抛出一个新对象,会产生对象副本
class MyException{};
void mydiv(int a,int b){
if (b==0) {
throw MyException();
}
}
int main(){
try {
mydiv(8, 0);
}
//会产生对象副本
catch(MyException e){
cout<<"MyException"<
抛出指针
class MyException{};
void mydiv(int a,int b){
if (b==0) {
MyException* e1;
throw e1;
}
}
int main(){
try {
mydiv(8, 0);
}
catch(MyException* e1){
cout << "MyException指针" << endl;
// delete e1;
}
return 0;
}
void mydiv(int a, int b) throw (char*, int) {
if (b == 0) {
throw "除数为零";
}
}
out_of_range(“超出范围”)
invalid_argument(“参数不合法”)
throw exception()
#include
class NullPointException
{
private:
char* message;
public:
NullPointException(char* str = "A problem") : message{str} {}
string_view what() const { return message; }
};
void mydiv(int a,int b){
if (b>10) {
throw out_of_range("超出范围");
}
else if(b==NULL){
throw NullPointException("为空");
}
else if(b==0){
throw invalid_argument("参数不合法");
}else{
throw exception();
}
}
int main(){
try {
mydiv(1, 1);
} catch (NullPointException &e2) {
cout<
class Err{
public:
class MyException{
public:MyException(){
}
};
};
void mydiv(int a,int b){
if (b>10) {
throw Err::MyException();
}
}
static_cast 普遍情况
const_cast 去常量
dynamic_cast 子类类型转为父类类型
reinterpret_cast 函数指针转型,不具备移植性
//C
char *c_p2 = (char *) func(2);
//C++意图明显
char *c_p = static_cast (func(2));
示例:
void *func(int type) {
switch (type) {
case 1: {
int i = 9;
return &i;
}
case 2: {
char a = 'X';
return &a;
}
default:
return NULL;
}
}
int main() {
int i = 0;
//原始类型转换(自动转换)
//原始类型转换,所有情况都是一种写法,可读性不高,有可能有潜在的风险
double d = i;
// double d = (double) i;
cout << d << endl;//0
//C
char *c_p2 = (char *) func(2);
cout << *c_p2 << endl;//X
//C++意图明显
char *c_p = static_cast (func(2));
cout << *c_p << endl;//X
int j = 0;
d = 9.5;
j = static_cast(d);
cout << j << endl;//9
return 0;
}
//c
char *c_p1 = (char *) c;
//c++
char *c_p = const_cast(c);
去常量可以编译const常量字符数组,示例:
void func(const char c[]) {
// c[1] = 'a';//常量数组不可修改
//c,其他人并不知道,这次转型是为了去常量
char *c_p1 = (char *) c;
c_p1[1] = 'X';
cout << c << endl;//hXllo
//c++
char *c_p = const_cast(c);
c_p[1] = 'Y';
cout << c << endl;//hYllo
}
int main() {
char c[] = "hello";
func(c);
return 0;
}
Man *m = (Man *) obj;
Man *m1 = dynamic_cast(obj);
Women、Man向上转型为Person会成功
Person向下转型为Man,根据原始类型可能成功,可能失败
class Person {
public:
virtual void print() {
cout << "人" << endl;
}
};
class Man : public Person {
public:
void print() {
cout << "男人" << endl;
}
void chasing(char *str) {
cout << str << "泡妞" << endl;
}
};
class Women : public Person {
public:
void print() {
cout << "女人" << endl;
}
void carebaby() {
cout << "生孩子" << endl;
}
};
void func(Person *obj) {
//调用子类的特有的函数,转为实际类型
//并不知道转型失败
Man *m = (Man *) obj;
m->chasing("c:");
//转型失败,返回NULL
Man *m1 = dynamic_cast(obj);
if (m1 != NULL) {
m1->chasing("c++:");
}
}
int main() {
Women w1;
Person *p1 = &w1;
func(p1);//c:泡妞(按理说这里都会转型失败,问题待排查)
Man m1;
Person *p2 = &m1;
func(p2);//c:泡妞、c++:泡妞
return 0;
}
//C方式
f_array[2] = (f_p) func2;
//C++ 方式
f_array[1] = reinterpret_cast(func2);
这里typedef void(*f_p)();函数指针无非保存的是一个地址,与void等类型没有关系
注意:reinterpret_cast这个操作符的转换几乎总是与编译平台息息相关,所以reinterpret_cast不具备移植性!
某些情况下可能会导致不正确的结果,应该尽量避免函数指针转型
void func1() {
cout << "func1" << endl;
}
char *func2() {
cout << "func2" << endl;
return "abc";
}
typedef void(*f_p)();
int main() {
//函数指针数组
f_p f_array[6];
//赋值
f_array[0] = func1;
//C方式
f_array[2] = (f_p) func2;
f_array[2]();//func2
//C++ 方式
f_array[1] = reinterpret_cast(func2);
f_array[1]();//func2
return 0;
}
ofstream:是outOfStream的意思
ifstream:是inOfStream的意思
下面示例会把“jack"\n"rose"写入到指定文本文件
int main() {
char *fname = "/Users/zhanglei/Desktop/text.txt";
//输出流
ofstream fout(fname);
//创建失败
if (fout.bad()) {
return 0;
}
fout << "jack" << endl;
fout << "rose" << endl;
//关闭
fout.close();
//读取
ifstream fin(fname);
if (fin.bad()) {
return 0;
}
char ch;
while (fin.get(ch)) {
//输出
cout << ch;
}
fin.close();
return 0;
}
示例会拷贝指定文件
int main() {
char *src = "/Users/zhanglei/Desktop/React Native eslint配置讨论.html";
char *dest = "/Users/zhanglei/Desktop/React Native eslint配置讨论_dest.html";
//输入流
ifstream fin(src, ios::binary);
//输出流
ofstream fout(dest, ios::binary);
if (fin.bad() || fout.bad()) {
return 0;
}
while (!fin.eof()) {
//读取
char buff[1024] = {0};
fin.read(buff, 1024);
//写入
fout.write(buff, 1024);
}
//关闭
fin.close();
fout.close();
return 0;
}
p1写成(char *) (&p1)是因为这里需要传入指针
下面实例实现了C++对象持久化的储存、读取
class Person {
public:
Person() {
}
Person(char *name, int age) {
this->name = name;
this->age = age;
}
void print() {
cout << name << "," << age << endl;
}
private:
char *name;
int age;
};
int main() {
Person p1("柳岩", 25);
Person p2("rose", 18);
//输出流
ofstream fout("/Users/zhanglei/Desktop/p_obj.data", ios::binary);
//指针能够读取到正确的数据,读取内存区的长度
fout.write((char *) (&p1), sizeof(Person));
fout.write((char *) (&p2), sizeof(Person));
fout.close();
//输入流
ifstream fin("/Users/zhanglei/Desktop/p_obj.data", ios::binary);
Person tmp;
fin.read((char *) (&tmp), sizeof(Person));
tmp.print();
fin.read((char *) (&tmp), sizeof(Person));
tmp.print();
return 0;
}
stl :standard template library 。
string字符串初始化方式、拼接、转c字符串、string常用api at
#include
int main() {
//初始化方式
string s1 = "I love ";
string s2("China");
//拼接
string s3 = s1 + s2;
cout << s3 << endl;//I love China
//转c字符串
const char *c_str = s3.c_str();
cout << c_str << endl;//I love China
cout << s1.at(2) << endl;//l
return 0;
}
#include
int main() {
//动态数据
//不需要使用动态内存分配,就可以使用动态数组
vector v;
v.push_back(12);
v.push_back(10);
v.push_back(5);
for (int i = 0; i < v.size(); ++i) {
cout << v[i] << endl;
}
return 0;
}
//12
//10
//5