命名空间
#include <iostream>
#include <stdio.h>
using namespace std;
namespace A { //namespace + X {} 命名空间的书写格式,自定义的命名空间A的域
int a;
float b;
void show(){
cout<<"hello"<<endl;
}
namespace B { //嵌套名字空间的一种方法
int a;
float b;
}
}
//为全局变量,在静态区,初始化了在.data区,没有初始化在.bss区,
//const修饰在常量区。rodata端;函数在代码段txt段
//全局变量调用就初始化
// cpye_data_section();
// clear_bss_section(); 将bss段为初始化的变量初始化为0
using namespace A;
//using A::a; // 如果程序中指向用到一个名字空间中的一个变量符的话,可以使用using 名字空间(域名)::变量名(函数名)
//using A::show;
int main() //一个进程运行,开辟4G空间
{
cout<<A::a<<endl; //::就是域解析符
cout<<a<<endl;
cout << "Hello World!" << endl;
show();
// cout<<A::B::A<<endl; // 使用::域解析符层层递进知道访问到变量
return 0;
}
c++结构体
#include <iostream>
using namespace std;
struct Stu{ //C++中struct主要存储数据信息,用于存放不同类型数据
int age;
string name;//智能对象,自动增加字符串长度;字符指针不能自动增加长度
int height;
void action(){// 在C中只能在结构体外部,定义函数指针,对函数指针进行赋值
cout<<"正在学习"<<endl;
}
};
int main()
{
// Stu stu;
// Stu stu{18, "zhangsan", 180};
Stu stu = {18, "zhangsan", 180};
// stu = {18,"zhangsan", 180};
cout<<stu.age<<endl;
cout << "Hello World!" << endl;
return 0;
}
/* 1、Stu stu{18, "zhangsan", 180}
* 2、Stu stu = {18, "zhangsan", 180}
* 3、Stu stu;
* stu = {18, "zhangsan", 180}
*/
c结构体
#include <stdio.h>
typedef void(*PFunc)(); //自定义的函数指针,类型与函数一致
struct Stu{
int age;
char* name;
int height;
PFunc action;
}; //通过函数指针调用结构体内元素
void WriteCode(){
printf("正在写代码\n");
}
int main()
{
struct Stu stu = {18, "zhangsan", 180, WriteCode};
stu.action();//调用stu的行为
printf("Hello World!\n");
printf("%d\n", stu.height);
return 0;
}
c字符串
#include <stdio.h>
int main()
{
// /0越界问题
char str1[] = {"hello"}; //编译器会自动在数组末尾加0;
char str2[] = {'h', 'e', 'l', 'l', 'o'};
char str3[] = "hello";
printf("%d\n", sizeof(str1)); // 6
printf("%d\n", sizeof(str2)); // 5
printf("%d\n", sizeof(str3)); // 6
char* str4 = "C++"; //只能访问,不能修改
return 0;
}
c++字符串
#include <iostream>
using std::cout;
using std::endl;
using std::string;
int main()
{
string str = "hello"; //C++尾部不带0
cout<<str.length()<<endl;
cout<<str.size()<<endl; //获取字符串长度
str.c_str(); //转换为C型字符串
cout << "Hello World!" << endl;
return 0;
}
// 函数、参数、返回值
宏函数
#include <stdio.h>
//宏函数,在预处理阶段,不参编译,更高效
#define Max(a, b) ((a)>(b)?(a):(b))
int main()
{
int a=10;
int b=11;
printf("%d\n", b++);
printf("%d\n", Max(a, b));
return 0;
}
//容易出错,c++内联函数
内联函数
#include <iostream>
using namespace std;
inline void show() //调用频率大,且简短的函数,高效
{
cout<<"hello "<<endl;
}
//函数调用先入栈,调用完再出栈
int main()
{
show();
cout << "Hello World!" << endl;
return 0;
}
// 内联函数和宏函数--------空间换时间
// 宏函数-----增强代码的复用性,在预处理阶段会将使用宏函数的位置用宏展开
// 减少了函数栈操作等开销,因此可以 调高程序运行效率。
// 内联函数-----在编译时会在调用内敛函数的地方进行展开(直接调用,省了栈操作),
// 没有函数的栈操作
// inline对编译器只是建议,编译器会自动进行优化。
// 定义在类中的函数数默认定义为内联函数
// 预处理 编译 汇编 链接
/* 预处理阶段:编译器把源文件包含在头文件、宏定义进行展开,生成预编译文件.i
* 编译阶段:编译器进行语法分析、语义分析,语法检查无误后把代码编译为汇编语言
* 将预编译文件.i转换为汇编代码,生成汇编文件.s
* 汇编阶段:把编译阶段生成的汇编文件转换为机器代码,生成目标文件.o
* 链接阶段:连接器将多个目标文件与其运行所需要的库进行链接,生成可执行文件.exe
*/
常函数
#include <iostream>
using namespace std;
const int c = 30; //静态区的只读数据段
void test()
{
const int a = 100; // 直接赋值,放在常量表中
const int* p = &a; //因为a类型为const int,加了取地址符&a,此时相当于const* int a,因为指针也是取地址;
*p = 300;
cout<<"a="<<a<<endl;
cout<<"*p="<<*p<<endl;
cout<<"a的地址:"<<&a<<endl;
cout<<"p指针的地址:"<<p<<endl;
/*
在C++中,当一个基本变量被const修饰时,编译器会在内存建立一个常量表,这个时候
a中的值就被保存到常量表中,当a再次被访问时,编译器会从常量表中读取这个值。
const修饰的值不在地址中,在常量表中。
const修饰的变量必须初始化。
*/
}
int main()
{
int a = 10;
static const int d = 40; //存储在静态区的只读数据段, 局部变量存在栈区
const int b = 20; //只读的变量,在栈区,如果是局部变量,代码块运行之后,变量释放
int f = 30;
cout << "Hello World!" << endl;
cout<< &a <<endl; //0x61fe8c
cout<< &b <<endl; //0x61fe88
cout<< &c <<endl; //0x404068
cout<< &d <<endl; //0x40407c
cout<<"--------------------------"<<endl;
int* p = &a;
*p = 100;
cout<<a<<endl; //通过指针修改变量中的值,此时a为100
const int* q = &a; //只读整形的指针,无法修改指针中的值,但可以访问a中的值
cout<<*q<<endl;
cout<<"----------------------------"<<end;
int* const p1 = &f; //p1是常指针,不允许改变指向,但可以通过修改p1来修改p1指向地址的值
// p1 = &a;
return 0;
}
/*
进程加载后,分配4G空间;如何分配
内存分为:
静态区:加载静态区,程序编译和链接的时候被加载。
如果程序中有static修饰的变量,或全局变量,这个时候就已经分配内存了
静态区又分为:代码段、只读数据段(被const修饰的全局变量、字符串)
.data段(全局初始化变量)、.bss段(全局未初始化变量)。
动态区;加载动态区---堆、栈、内核
堆:手动申请,手动释放。malloc, calloc,relloc 开辟空间,free(pointer)
栈:随用随释放,出了这个代码段,变量就被自动销毁。
*/
/*
const int 修饰类型,只读类型,变量值不能发生改变
*/
函数
#include <iostream>
using namespace std;
int show(int a=1, int b=2, int c=3) //默认值从右向左赋值
{ //函数在调用时,参数的赋值是从左向右一次进行赋值的
return a+b+c;
}
int main()
{
cout<<show()<<endl;
return 0;
}
// 程序运行:预处理(把字符展开,没有语法检测),编译,汇编,链接
函数重载
#include <iostream>
using namespace std;
void func(){
cout<<"这是函数1"<<endl;
}
void func(float a){
cout<<"这是函数3"<<endl;
}
void func(float a, int b){
cout<<"这是函数4"<<endl;
}
void fucn(int a){
cout<<"这是函数4"<<endl;
}
// 在C++中同一作用域内,一组函数名相同,参数列表不同(参数个数或类型不同),
// 返回值不受影响,这一组函数就会发生重载关系
// 函数重载的原理,在G++编译器中,编译器会根据函数名字的字符数 + 参数列表中的类型及顺序
// 自动重新生成一个新函数名并几率在符号表中,从而产生不同的函数名,当函数调用时,便根据这个记录
// 寻找符合要求的函数名,进行调用。
// 重载编译阶段就是把这样的函数名相同,但参数列表的函数进行了重命名,在函数被调用时,根据符号
// 表中的记录,对应的调用不同的函数。称为编译时的多态,静态多态
int main()
{ func();
func(3.14f);
func(1);
func(3.14, 5);
cout << "Hello World!" << endl;
return 0;
}
引用
#include <iostream>
using namespace std;
void swap(int& c, int& d){
int temp = c;
c = d;
d = temp;
}
int main()
{
int a= 100;
int *p = &a;
cout<<*p<<endl;
*p=888;
cout<<*p<<endl;
cout<<a<<endl;
//int* const pa = &a; const修饰pa,即pa固定指向a的地址,不能改变,但可以改变a中的值,const修饰的变量必须赋初值
int& b = a; //使用引用必须对引用的对象初始化
cout << "--------------------------------" << endl;
int c= 1;
int d= 2;
swap(c, d);
cout<<"c = "<< c<<endl;
cout<<"d = "<< d<<endl;
return 0;
}
动态内存
#include <iostream>
using namespace std;
void test1()
{
// 在堆区开辟空间
int *pa = (int*)malloc(sizeof(int)); // malloc分配的是void类型,并没有进行初始化
cout<<*pa<<endl;
int *pb = (int*)calloc(1, sizeof(int)); //分配空间,并初始化
cout<<*pb<<endl;
free(pa); // 释放开辟的空间
pa = NULL; //让指针重新指向空
free(pb);
pb = NULL;
}
// 在C++中用new关键字来动态开辟堆区空间,当这块空间不再使用时,一定要使用delete关键字释放空间,
// delete释放指针指向的内存,并不是指针本身所占用的内存,所以指针指向的那块区域并未清零
void test2()
{
int* pa = new int;
cout<<*pa<<endl;
int* pb = new int(100); //动态申请空间,并初始化为100
int* c = new int[4]; //连续开辟了4个int空间
c[0] = 10;
c[1] = 100;
cout<<*c<<endl;
cout<<*(c+1)<<endl; //指针偏移
delete pa;
delete pb;
delete []c; // 释放连续申请的空间
pa = nullptr;
pb = nullptr;
}
int main()
{
test1();
test2();
cout << "Hello World!" << endl;
return 0;
}
类
#include <iostream>
using namespace std;
class Person
{
string name;
int age;
char sex;
public:
Person() = default;
Person(string _name, int _age, char _sex)
{
name = _name;
age = _age;
sex = _sex;
}
void walk(){
cout<<"walk"<<endl;
}
void getBread()
{
cout<<"eat"<<endl;
}
void setName(const string& _name){
// C++不允许临时对象被修改或者重新赋值,所以对临时对象的引用必须是const类型
// 一个非const引用,只能引用与其类型完全相同的对象,或者是其派生类的对象
name = _name;
}
string getName()
{
return name;
}
void showInfo(){
cout<<"人物信息:"<<name<<endl;
}
};
int main()
{
Person p1; // 栈区
Person *p2 = new Person;
Person p3("zhangsan", 18, 'M'); //字符串除了用string类型,就只能使用char类型数组了
p3.setName("lisi");
p3.showInfo();
cout << "Hello World!" << endl;
return 0;
}
内存对齐
#include <iostream>
using namespace std;
enum Sex
{
female,
male
};
// #pragma pack(1)
class Stu
{
private:
string name;
int age;
Sex sex; // 枚举类型,默认是整形为4个字节
char ff; // 加一个char,由于类中最大字节数为4,故占8个字节
public:
void writeCode()
{
cout<<"happy happy to write code"<<endl;
}
void eatLunch()
{
cout<<"happy to eat"<<endl;
}
// 公有方法访问私有数据
void setName(const string& name) // string 智能对象 很大,传地址,少一份拷贝
{
this->name = name; //this 本对象
}
string getName()
{
return this->name;
}
void setAge(int age) //int 很小,不需要引用
{
this->age = age;
}
int getAge()
{
return this->age;
}
void setSex(Sex sex)
{
this->sex = sex;
}
Sex getSex()
{
return this->sex;
}
Stu(){}
~Stu(){}
};
// #pragma pack()
int main()
{
Stu stu;
/*cout<<sizeof(stu)<<endl;
cout<<sizeof(string)<<endl; // 占24个字节,指针占8个字节
cout<<sizeof(Sex)<<endl; */ // 占4个字节
// 1、求出这个类中成员内存最大的那一个,以这个内存大小为基准,进行内存字节对齐
// 2、如果是结构体或数组,结构体和数组是连续存储的,求每个元素中最大的空间,求到最长的那个元素为基准。
stu.setName("zhangsan"); //常量,左值引用
return 0;
}
值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。
指针传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作
引用传递:形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
常对象
#include <iostream>
using namespace std;
class Plane
{
private:
int height;
int width;
public:
int outArea()const // 在函数体前加const常函数,在函数前加const,返回值为只读类型
{
return this->width * this->height;
}
void setValue(int width = 1, int height = 1)
{ // 函数重载不设定初始值
this->width = width; //this类中的属性
this->height = height;
}
};
bool compared(const Plane& p1, const Plane& p2) // c++中不允许临时对象被修改或重新赋值
{
return p1.outArea()>p2.outArea() ? true: false;
}
int main()
{
Plane p1;
p1.setValue(2, 3);
cout<<p1.outArea()<<endl;
Plane p2;
p2.setValue(3, 4);
if(compared(p1, p2))
{
cout<<"p1 is big"<<endl;
}else{
cout<<"p2 is big"<<endl;
}
return 0;
}
// 被const修饰的对象叫做常对象,只能调用常函数
// 常函数不允许对类中的对象进行修改,保证属性的安全
析构函数
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
public:
// Stu() //默认空构造
// {
// cout<<"wucan kong gouzao "<<endl;
// }
Stu(string name="", int age=0)
{
this->name = name;
this->age = age;
cout<<"I am "<<this->name<<", "<<this->age<<endl;
}
};
int main()
{
// Stu stu1; // 隐式调用在栈区,会调用无参构造,调用无参构造的对象一定不要加括号。
// Stu stu2(); //这是一个函数声明,不是定义对象
// Stu* stu = new Stu; // 这是在堆区的隐式调用,在调用无参构造时,可以加括号,也可以不加,一样的前提是手写了无参构造。
cout << "*******************************************************" << endl;
// Stu stu3("zhangsan", 18); //显示调用
Stu* stu4 = new Stu(); //显示调用
return 0;
}
// 1、构造函数所存在的意义? 初始化类中的属性。
// 2、构造函数的调用时机:当类对象在内存被定义,会自动调用对应的构造函数
// 3、如果类中没有写构造函数,编译器会自动添加一个默认构造函数,
// 这个构造函数版本会根据对象调用方式不同而不同
// 如果在栈区定义的无参对象,那么编译器会自动调用空构造
// 如果实在堆区定义的无参对象,那么编译器会自动调用有参,且有默认值得构造
// 相当于类中的属性进行了初始化
析构函数
#include <iostream>
using namespace std;
class B
{
public:
B()
{
cout<<"B的构造"<<endl;
}
~B()
{
cout<<"B的析构"<<endl;
}
};
class A
{ B* b;
public:
A()
{
b = new B;
cout<<"A的构造"<<endl;
}
/*
* 1、析构函数的定义格式:~+类名(){释放空间的逻辑}
* 2、析构函数的用途:当类中有指针的情况,有来释放指针指向的堆区空间
* 3、析构函数的调用时机:当对象销毁时,自动调用析构函数
* 4、析构函数的意义:专门用来清理类中有指针的成员(该成员指向堆区的情况)
*/
~A()
{
cout<<"A的析构"<<endl;
delete b;
}
};
int main()
{
A a; //在栈区,出栈后自动调用析构函数
A* pa = new A;
delete pa;
cout << "Hello World!" << endl;
return 0;
}
初始化列表
#include <iostream>
using namespace std;
class A
{
private:
const int a;
static int b;
public:
// 初始化列表:是构造函数中特有的语法,格式是在构造函数的参数列表后:只读变量名(所要的赋值)
// 初始化列表的用途,主要用来初始化一些需要构造对象前就要初始化一些类属性
// 初始化列表的调用时机,是先于构造函数的 ,因为const修饰的变量必须进行初始化
A():a(10)
{
cout<<"A的构造"<<endl;
}
/*
* 1、析构函数的定义格式:~+类名(){释放空间的逻辑}
* 2、析构函数的用途:当类中有指针的情况,有来释放指针指向的堆区空间
* 3、析构函数的调用时机:当对象销毁时,自动调用析构函数
* 4、析构函数的意义:专门用来清理类中有指针的成员(该成员指向堆区的情况)
*/
~A()
{
cout<<"A的析构"<<endl;
}
int getAInfo()
{
return a;
}
};
// 静态变量的初始化格式:类外,main前进行初始化 格式:类型名+类域::静态变量名 = 值
int A::b = 10;
int main()
{
// int A::b = 10;
A a;
cout<<a.getAInfo()<<endl;
cout << "Hello World!" << endl;
return 0;
}
// 静态区的数据在进入main前,必须赋值
// 函数进栈入栈:变量先入栈
// 类中的属性为静态变量时,必须在类外进行首次初始化,static修饰的变量只能修饰一次
// static修饰的类变量是属于整个类的,所有在动态区定义对象共享
// 当静态数据内容发生改变时,动态区中所有对象中的该属性同时发生改变。
// static修饰的函数也是这样,静态函数不可以使用类中非静态成员变量
//
// static修饰全局变量时,声明和定义是同时给出的;而extern一般是定义和声明分开,且只能定义一次
// static作用域是自身编译单元(即一个.c文件以及这个.c文件所包含的.h文件);而exter的全局作用域是整个工程(一个工程可能包含很多个.h和.c文件)
分文件编程
#ifndef STU_H
#define STU_H // 如果没有则定义
//#pragma once
#include <iostream>
using namespace std;
class Stu
{
string name;
int age;
static int score; //类内声明,所有对象共享
public:
Stu() = default;
Stu(string name, int age);
~Stu()=default;
void showInfo();
int getScore();
};
#endif // STU_H
#include "stu.h"
Stu::Stu(string name, int age)
{
this->name = name;
this->age = age;
cout<<"multiply param"<<endl;
}
void Stu::showInfo()
{
cout<<"Student name: "<<this->name<<"age: "<<this->age<<endl;
}
int Stu::getScore()
{
return score;
}
int Stu::score = 100;
#include <iostream>
#include "stu.h"
using namespace std;
int main()
{
Stu stu1;
Stu stu2("zhangsan", 18);
stu1.showInfo();
cout<<"--------------------------------"<<endl;
stu2.showInfo();
cout << "Hello World!" << endl;
cout<<"----------------------------------"<<endl;
cout<<stu2.getScore()<<endl;
return 0;
}
拷贝构造
#include <iostream>
#include <stu.h>
using namespace std;
int main()
{
Stu stu1("zhangsan", 18);
Stu stu2 = stu1; // 拷贝构造
stu2.showInfo();
Stu stu3(stu2); // 直接进行拷贝赋值的拷贝为浅拷贝
// 三次拷贝,拷贝对象都是指向同一片内存,
// 当程序结束时,调用三次析构,多次释放空间
cout << "Hello World!" << endl;
return 0;
}
/*
* 1、拷贝构造也是一种构造
* 必须自定义写一个构造函数,编译器只能提供一个构造函数
* 没写无参构造,那么编译器也不会提供无参构造。与默认构造也会产生重载关系。
* 2、拷贝构造的调用时机
* 当定义的对象需要另一个对象进行初始化,默认调用系统默认拷贝构造函数。
* 3、拷贝构造的书写格式
* Stu(Stu& stu)
* {
* this = stu;
* }
* 4、默认拷贝构造为浅拷贝
* 浅拷贝:只是另一个对象中属性的副本,在有指针的情况下,就会产生
* 指向同一空间问题。对拷贝构造进行改造,需要重新定义一块新的堆区空间
* 并把此空间的内容拷贝到新开辟的同样的空间之中。这种方式称为深拷贝。
* 深拷贝使用时机:类中有指针的情况,且该指针指向堆区空间,为了避免二次
* 释放问题。
* 5、拷贝构造的调用时机
* 当定义的对象要被另一个对象进行初始化时,默认调用系统的拷贝构造
* 当函数参数为对象时,也会调用拷贝构造
* 当函数返回值为对象时,也会调用拷贝构造
*/