C++ 初学习01

 结构体与C的区别


  • 类型上不再需要struct关键字,直接用结构体名即可
  • C++结构体中允许函数存在:
    • ①在结构体中声明,在结构体外实现(也可以直接在结构体内实现);
    • ②结构体函数中访问数据 ,是可以直接访问的;
    • 调用(和数据成员方式一样):
      • (1)对象(结构体变量).成员
      • (2)对象指针 -> 成员
      • (3)(*对象指针).成员
  • C++在没有写构造函数和权限限定时,用法和C语言一样

内存池


#include <iostream>

using namespace std;
//允许申请一段内存,供给程序使用,综合管理内存
//malloc 内存是在堆区
//calloc 内存是在自由储存区

void testMemory()
{
    char* memorySum = new char[1024];

    //int* pNum = new(申请内存的开始位置) int[3];
    int* pNum = new(memorySum) int[3]{1,2,3];

    //char* pstr = new(pNum+3) char[20]{"Iloveyou"};//与下面一句等效
    char* pstr = new(memorySum+12) char[20]{"Iloveyou"};   


    for(int i = 0; i < 3; i++)
    {
        cout<<pNum[i]<<" ";

    }
    cout<<endl;
    
/*等效for(int i = 0; i < 3; i++)
 *   {
 *      cout<<((int*)memorySum)[i]<<" ";
 *
 *   }
 */  cout<<endl<<pstr<<endl;
     cout<<(memorySum+12)<<endl;
    
    delete[] memorySum;
    memorySum = nullptr;
}
int main()
{
    testMemory();
    return 0;
}

string类型


  • string创建
    • ①带初始化
    • ②不带初始化
    • ③通过另一个字符串创建
  • string基本操作:①拷贝;②赋值;③连接;④比较。
  • to_string()函数
    string str1 = "Iloveyou";
    
    string str2 = to_string(1234);
    
    cout<<str2<<endl; 

函数


函数 功能
capacity() 容器能储存数据的个数
size() 容器目前存在的元素数
reserve() 指定容器能储存数据的个数
resize() 重新指定有效元素的个数,区别reserve()在指定容量的大小

 类和对象



//关键字  类名
Class   classname
{
    Access specifiers:           //访问修饰符,有三种public   protected  private

    Data members/variables;       //数据成员/变量

    Member functions(){}          //方法(成员函数)


}
  • 成员函数的声明在类中,而定义一般在类外;
    • 对于类中的成员函数的使用需要加上类名限定,如Box::get(void),其中Box是类名,"::"是访问符号,又叫范围解析运算符或域操作符,get()是类中声明的函数,void是参数;
    • 对于类中的数据成员/变量由于权限限定,一般不能直接拿来使用,需要重新定义一组相对应的数据成员/变量在函数中进行调用,如length = len; breadth = bre; height = hei;
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)           //::是域操作符,Box是类名,get()是类中的成员函数,void是参数
{
    return length * breadth * height;
}
 
void Box::set( double len, double bre, double hei)   //重新定义相应的数据成员/对象
{
    length = len;                         
    breadth = bre;
    height = hei;
}
  • 调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据;
    Box myBox;          // 创建一个对象
     
    myBox.get();  // 调用该对象的成员函数

 类构造函数和析构函数


  • 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。作用:构造函数可用于为某些成员变量设置初始值。
    • 默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值。
    • 任何类中都只存在一个默认的构造函数,当我们自己写了构造函数后,默认的就不存在的。但是可以写不止一个构造函数,写出含有不同数量参数的构造函数,如
      class Ti
      {
      public:
      
          Ti(string _name,int _age){              //第一个构造函数
              name = _name;
              age = _age;
              cout<<"带参构造函数"<<endl;
          }
      
          Ti(){
              cout<<"无参构造函数"<<endl;
          }                                  //第二个构造函数
      
      protected:
      
          string name = "Jone";
          age = 18;
      
      }
      
      int main()
      {
          Ti skt("jone",28);                     
          Ti rng;
      }

    • 构造函数有几个参数,在主函数中创建相应的对象的时候也需要写上相应数量的参数,如
      class Ti
      {
      public:
      
          Ti(string _name,int _age){            //见下
              
              name = _name;
              age = _age;
              
          }
      
      protected:
      
          string name = "Jone";
          age = 18;
      
      }
      
      int main()
      {
          Ti ti("jone",28);                     //参数数量需要与类中构造函数定义的参数数量相同
      
      
      }

    • 构造函数可以直接使用delete删掉,在类中操作,如Line()=delete;
    • 为了构造不同模样的函数,可以在类中对构造函数进行缺省处理,使得在主函数中使用构造函数是不必局限于固定的参数数量(不超过最大数量):
      class girl
      {
      public:
      
          girl(string _name = "",int _age = 18){      //""内的部分省略了,于是主函数中相应的构造函
              name = _name;                           //数不超过两个的,都可以创建,如0,1,2个参数
              age = _age;
          }
          
      protected:
          string name;
          int age;     
      
      };
      
      int main()
      {
          girl girl1;                                   //无参数
          girl girl2("Lisa");                           //一个参数
          girl girl3("Kasa",19);                        //两个参数
      
          return 0;
      }

    • 使用默认的构造函数可以使用default,如Line()=default;
      class Line
      {
         public:
            void setLength( double len );
            double getLength( void );
            Line() = default;   // 使用默认的构造函数
      
       
         private:
            double length;
      };
    • 对于构造函数可以使用初始化列表来初始化字段格式如下    构造函数名(参数1,参数2,...):成员1(参数1),成员2(参数2),...{}
      class Line
      {
         public:
            void setLength( double len );
            double getLength( void );
            Line();   // 这是构造函数声明
          //Line() = delete;   //删除构造函数
       
         private:
            double length;
      };
      
      Line::Line( double len): length(len)   //使用初始化列表来初始化字段
      {
          cout << "Object is being created, length = " << len << endl;
      }
      
      //等效于下面未使用初始化列表的初始化方式
      
      Line::Line( double len)
      {
          length = len;
          cout << "Object is being created, length = " << len << endl;
      }
       
      • 可以用初始化参数列表的方式来使用构造函数调用另一个构造函数,也叫做委托构造;
        class student
        {
        pubilc:
            GIF(string name,int age):name(_name),age(_age) {}
        
            //通过初始化列表的方式用构造函数调用另一个构造函数,也叫做委托构造
            GIF():GIF("名字",18) {}                  
            
            void print
            {
                cout<<name<<"\t"<<age<<endl;
            }
        
        protected:
            string name;
            int age;
        
        }

  • 类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
    • 当类中的数据成员是指针进行动态内存申请了内存时,需要手写析构函数。
    • 析构函数一般不需要手动调用,析构函数的调用不代表生命周期的结束。

拷贝构造函数

  • 拷贝构造函数也是构造函数,长相和构造函数一样,不过参数是固定的;
    • 拷贝构造函数的作用是通过一个对象去初始化另一个对象,即当通过一个对象去创建另一个新的对象时需要调用拷贝构造函数;
    • 即使不写拷贝构造函数,也存在一个默认的拷贝构造函数//后面浅拷贝会讲;
    • 区分函数是否属于拷贝构造函数的重要依据,是否创建了对象,对比参数(MM mm)与(MM& mm);

    • 注意写拷贝构造函数是的const 修饰,若不修饰,在对匿名对象进行赋值时可能会报错;
    • //对比上面一个代码框的代码(默认的拷贝构造函数),现在这个是手写的拷贝构造函数
      class MM
      {
      public:
          MM(string _name,int _age) :name(_name),age(_age) {}
          void print()
          {
              cout<<name<<"\t"<<age,,endl;
          }
      
          //拷贝构造函数
          MM(const MM& mm)                   //MM girl(mm);   //const作用看下方匿名对象
          {
              name = mm.name;                //girl.name = mm.name;
              age = mm.age;                  //girl.age = mm.age;
              cout<<"拷贝构造"<<endl;
          }
          //右值引用拷贝构造           //当使用右值引用操作后,主函数中创建对象是不可以进行赋值操作
          //MM(MM&& mm)
          //{
          //    name = mm.name;                //girl.name = mm.name;
          //    age = mm.age;                  //girl.age = mm.age;
          //    cout<<"右值引用拷贝构造"<<endl;        
          //}
      
      protected:
          string name;
          int age;
      }
      
      void printData(MM mm)        //MM mm = 实参;创建了一个对象,这一种调用了拷贝构造函数
      {
          mm.print();
      }
      void printData2(MM& mm)      //调用形参,没有创建对象,不存在拷贝本,没有调用拷贝构造函数
      {
          mm.print();
      }
      
      int main()
      {
          MM mm("mm",18);
          mm.print();
      
          //显式调用
          MM girl(mm);                    //现在调用的是上面手写的拷贝构造函数
          girl.print();
      
          //隐式调用
          MM girl2 = mm;
          girl2.print();
      
          //注意:这种不是拷贝构造,而是运算符重载
          MM girl3;
          girl3 = mm;
          girl3.print();   
      
          //无名对象  匿名对象                 
          MM temp;                 
          temp = MM("匿名",18);            //进行右值操作后不能进行赋值操作
          temp.print();
      
          //匿名对象创建对象的时候,一定要用const修饰
          MM temp2 = MM("匿名",19);   //用右值操作可以,上方构造函数创建对象没用const修饰不可以
          temp.print();               
      
      
      }

深浅拷贝

  • 浅拷贝:默认的拷贝构造叫做浅拷贝,因为会出现析构问题(重复释放内存问题)。
  • 深拷贝:在构造函数中进行动态内存申请,也就是在构造函数中做new操作,并且进行拷贝赋值(strcpy)的操作。
//由于浅拷贝会出现析构问题(重复释放内存)而出现错误导致程序崩溃
//如若不进行深拷贝,创建的三个对象里的name指针指向了同一个内存,即最初开辟的"baby"所指向的内存区域

#include<iostream>
#include<string>
#include<cstring>

using namespace std;

class MM
{
public:
    MM(const char* mname, int age) :age(_age)
        {
            name = new char[strlen(mname) + 1];
            strcpy_s(name,strlen(mname) + 1, mname);
        }

    void prnt()
        {
            cout<<name<<"\t"<<age<<endl;
        }
    MM(const MM& object)             //通过赋值的方式,也是浅拷贝,加上动态内存申请就是深拷贝
        {
            name = object.name;
           //深拷贝需要做动态内存申请,也就是重新new,strcpy一遍
            {
                name = new char[strlen(object.name) + 1];             
                strcpy_s(name,strlen(object.name) + 1,object.name);//+1是字符串结束标记
            }   
            age = object.age;
        }
    ~MM()
        {
            delete[] name;
        }

protected:
    char* name;
    int age;
}

int main()
{
    MM mm("baby",19);
    //下面两种都叫浅拷贝,都是通过默认的拷贝构造创建的对象
    MM girl(mm);
    MM gm = mm;

    mm.print();
    girl.print();
    gm.print();

    return 0;
}

构造顺序和析构顺序问题

  • 普通对象的构造顺序和析构顺序是相反的;
  • new出来的对象,在delete的时候会直接调用析构;
  • static对象,当程序关闭时释放;
#include<iostream>
#include<string>

using namespace std;

class MM
{
public:
    MM(string name = "x") :name(name)
        {
            cout<<name;
        }
    ~MM()
        {
            cout<<name;
        }

protected:
    string name;
}

int main()
{
    {   //普通变量的构造顺序和析构顺序相反
        MM mm1("A");              //A
        static MM mm2("B");       //B    static定义的变量在程序关闭的时候生命周期才会结束
        MM* p3 = new MM("C");     //C
        MM mm4[4];                //xxxx
        delete p3;                //C    delete 直接调用析构函数
        p3 = nullptr;
                                  //xxxxAB
    }
    //最终结果:ABCxxxxCxxxxAB


    return 0;
}

C++结构体构造函数

  • 结构体与类有相像之处,也有不同之处;
#include <iostream>
#include <string>

using namespace std;

struct MM
{
    //默认为公有属性,权限限定词(public/protected/proviate)也是可以用的
    string name;
    int age;
    //MM(string name) :name(name)
    //{
    //    cout<<"构造函数"<<endl;
    //}

};


int main()
{
    //采用创建时候赋值的方式,也是调用构造函数
    
    MM object = {"Lisa",18};      //如果这么写,需要在上面struct中写一个含有两个参数的构造函数
    cout << object.name << "\t" << object.age << endl;


}

猜你喜欢

转载自blog.csdn.net/qq_59470001/article/details/127403691