C++基础(一)

命名空间

#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、拷贝构造的调用时机
 * 当定义的对象要被另一个对象进行初始化时,默认调用系统的拷贝构造
 * 当函数参数为对象时,也会调用拷贝构造
 * 当函数返回值为对象时,也会调用拷贝构造
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值