c++语法

2023-11-11

文章目录


实用链接
C++语言中文网
实例用法查看网址
c++在线编译器
google代码风格规范

0.0 编译运行

  • .c是C语言代码,.cpp是C++代码
  1. g++是执行c++文件的命令;gcc是执行c文件的命令
  2. bin文件运行
chmod +x filename.bin
./filename.bin ....

单个程序编辑调试

  • step 1: 写一个xx.cc或者xx.cpp的文件

  • step 2:编译文件g++ xx.cpp(编译单个文件),g++ xx1.cpp xx2.cpp(编译多个文件)

  • step 3:生成对应的可执行文件g++ xx1.cpp -o demodemo是可执行文件的名字
    如果c++文件有错误是不会生成对应的可执行文件的

  • step 3: ./demo运行文件(这样操作,才能看到里边的printf等信息,否则是没有的)

  • linux环境下,‘ctrl+d’是输入结束的字符

库文件编译调试

  • step 1: 一般新建一个build文件夹(其他也可以),用于存放编译过程中产生的文件
  • step 2 : cmake dir, dir是CMakeLists.txt文件所在的路径,cmake是跨平台编译的工具;
  • step 3: 然后会在当前路径(build)下生成一个Makefile的文件,执行make即生成对应的可执行文件
  • step 4: 对于具体的可执行文件的使用方式
>>>./demo_vc 
>>>输出    ./demo_vc vc.dat input.pcm lpcnet_feats.bin

1. 变量

1.1 变量的声明和定义

定义包含声明,声明不包含定义
1.变量的定义:变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。

2.变量的声明:用于向程序表明变量的类型和名字。程序中变量可以声明多次,但只能定义一次。

3.两者联系与区别

(1)定义也是声明,因为当定义变量时我们也向程序表明了它的类型和名字;

(2)但声明不是定义,可以通过使用extern关键字声明变量而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern;extern位于函数外部的时候,才可以声明+定义,在函数内部不允许

4. 为什么要区分声明和定义
C++程序通常由许多文件组成。为了让多个文件访问相同的变量,C++区分了声明和定义
需要被别的文件用的变量,必须在本文件中定义好,比如在文件1中定义extern int i=0;然后才可以再别的文件中使用,使用方式是:在类体的外部使用extern int i;别的文件只能声明,而不能再定义


#include <iostream>
using namespace std;
 
// 变量声明   变量声明可以注释
extern int a, b;
extern int c;
extern float f;
  
int main ()
{
  // 变量定义   变量定义不能注释
  int a, b;
  int c;
  float f;
 
  // 实际初始化
  a = 10;
  b = 20;
  c = a + b;
 
  cout << c << endl ;
 
  f = 70.0/3.0;
  cout << f << endl ;
 
  return 0;
}

1.2 变量的作用域

全局变量: 在所有函数外部声明的变量
局部变量: 在函数或一个代码块内部声明的变量,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
形式参数: 在函数参数的定义中声明的变量

变量的作用域有限,同时出现的时候,作用域小的屏蔽作用域大的,但是作用域小的结束作用区间后就失效了

#include <iostream>
using namespace std;
int b=5   ## 全局变量
int main()
{
    int b = 2;
    {
        int b = 1;
        cout << "b = " << b << endl;   // 作用域结束即失效
    }
    cout << "b = " << b << endl;
    cout << ::b << endl;   // 5 ## 通过域名在函数中引用到全局变量,不加域名解析则引用局部变量   
}

1.3 namespace命名空间

命名空间namespce定义:程序员各自在写代码时,可能会声明相同名字的函数或者变量,这样代码整体编译的时候就会报错—通过命名空间解决这一问题。
格式:

namespace name----命名空间的名字{
    //variables, functions, classes---变量/函数/define等
}

使用方法:

第一种:直接调用
Li :: fp = fopen("one.txt", "r");  //使用小李定义的变量 fp
Han :: fp = fopen("two.txt", "rb+");  //使用小韩定义的变量 fp

第二种: using调用
using Li :: fp;  --------“::”域操作解析符
fp = fopen("one.txt", "r");  //使用小李定义的变量 fp
Han :: fp = fopen("two.txt", "rb+");  //使用小韩定义的变量 fp

第三种:namespacea整体调用
using namespace Li;
fp = fopen("one.txt", "r");  //使用小李定义的变量 fp
Han :: fp = fopen("two.txt", "rb+");  //使用小韩定义的变量 fp

标准空间std

定义:将类、函数、宏等都统一纳入一个命名空间,这个命名空间的名字就是std。std 是 standard 的缩写,意思是“标准命名空间”。
使用:如果是在函数体中声明using namespace std;,那么作用域只在这个函数内部有效;如果想要全局有效,需要开始声明(但是可能会有冲突,不建议)。

2. 关键字

2.1 extern

extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
extern 用法说明

3. 常量

C++中设置一直不变的量叫常量,一般用大写字符表示常量
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值

1. define 定义

#define identifier value define 预处理器
#define WIDTH  5
#define NEWLINE '\n'

2. const 定义

const type variable = value; 声明指定类型的常量
const int  WIDTH  = 5;
const char NEWLINE = '\n';

3. 区别

void f1()
{
  #define N 12
  const int n = 12;

  #undef N //取消宏定义后,即使在f1函数中,N也无效了
  #define N 21//取消后可以重新定义
}
void f2 ()
{
    cout<<N <<endl; //正确,N已经定义过,不受定义域限制
    cout<<n <<endl; //错误,n定义域只在f1函数中
}

4. 函数

  1. 函数调用的顺序:C/C++ 编译 cpp 文件是从上往下编译,所以 main 函数里面调用其他函数时,如果其他函数在 main 函数的下面,则要在 main 函数上面先声明这个函数。
    或者把 main 函数放在最下面,这个不仅限于 main 函数,其他函数的调用都是如此。被调用的函数要在调用的函数之前声明。

函数定义

return_type function_name( parameter list )
{
   body of the function
}
  • 返回类型: 一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
  • 函数名称: 这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数: 参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • 函数主体: 函数主体包含一组定义函数执行任务的语句。
    其中,parameter list中参数的名称可以省去不写,但是参数的类型必须声明

参数的传递方式

在这里插入图片描述

默认参数

定义:所谓默认参数,指的是当函数调用中省略了实参时自动使用的一个值,这个值就是给形参指定的默认值。

# 正确使用
void func(int a, int b, int c=20){ }

# 错误使用:默认参数只能放在形参列表的最后,实参和形参的传值是从左到右依次匹配的,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。
void func(int a, int b=10, int c, int d=20){ }

函数重载

  • 定义: 在实际开发中需要实现几个功能类似的函数,只是有些细节不同。比如交换两个数据(int/float/char等),不必要重新命名,可以让多个函数拥有相同的名字,只要它们的参数列表不同就可以。
  • 函数的重载的规则:
    (1)函数名称必须相同。
    (2)参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
    (3)函数的返回类型可以相同也可以不相同。
  • 函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。

引用调用

向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

#include <iostream>
using namespace std;
 
// 函数声明
void swap(int &x, int &y);
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;
 
   /* 调用函数来交换值 */
   swap(a, b);
 
   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;
 
   return 0;
}

// 函数定义,实际上无返回值,但传参过来的a,b被修改了
void swap(int &x, int &y)
{
   int temp;
   temp = x; /* 保存地址 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y  */
  
   return;
}

指针调用

#include <iostream>
using namespace std;

// 函数声明,传递的指针相当于一个参数
void swap(int *x, int *y);

int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;

   /* 调用函数来交换值
    * &a 表示指向 a 的指针,即变量 a 的地址 
    * &b 表示指向 b 的指针,即变量 b 的地址 
    */
   swap(&a, &b);

   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;
 
   return 0;
}

// 函数定义
void swap(int *x, int *y)
{
   int temp;
   temp = *x;    /* 保存地址 x 的值 */
   *x = *y;        /* 把 y 赋值给 x */
   *y = temp;    /* 把 x 赋值给 y */
  
   return;
}

setw() 函数

setw() 函数用于设置字段的宽度,仅对后方最近的起作用

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    // 开头设置宽度为 4,后面的 runoob 字符长度大于 4,所以不起作用
    cout << setw(4) << "runoob" << endl;
    // 中间位置设置宽度为 4,后面的 runoob 字符长度大于 4,所以不起作用
    cout << "runoob" << setw(4) << "runoob" << endl;
    // 开头设置间距为 14,后面 runoob 字符数为6,前面补充 8 个空格
    cout << setw(14) << "runoob" << endl;
    // 中间位置设置间距为 14 ,后面 runoob 字符数为6,前面补充 8 个空格
    cout << "runoob" << setw(14) << "runoob" << endl;
    return 0;
}

代码输出结果为

runoob
runoobrunoob
        runoob
runoob        runoob

5. 数组/字符串

5.1 数组

定义:

double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
或者 
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

容器vector

  • 初始化 std::vector<double> values;
  • 元素增减
#include <vector>
using std::vector
vector.push_back(),尾部添加
vector.pop_back() 尾部移除
  • vector 输出不能一次性输出,循环打印
vector<int> path;
for (auto i : path)
    std::cout << i << ' ';
  • 容器的长度int length = vector.size()-1

5.2 字符串

定义

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
或者
char greeting[] = "Hello";

在这里插入图片描述

char

  • char是单个的字符
char a='a' ----注意使用的是单引号
string m="aaa" ----string使用的是双引号

5.3 string类

  • string变量的定义
string s1;   ******只是定义但没有初始化,默认是“”***********
string s2 = "c plus plus";
string s3 = s2; 
string s4 (5, 's'); ********s4='sssss'********
  • 字符串长度(string 的末尾没有’\0’字符)
string s2 = "sss";
int len = s.length();      *******len=3****
int len = s.size()  两种方法都可以

  • 可以调用C语言的fopen,但是需要将字符串转成c类型string.c_str()
string path = "D:\\demo.txt";
FILE *fp = fopen(path.c_str(), "rt");
  • string的输入输出:输入运算符>>默认会忽略空格,遇到空格就认为输入结束
using std::string;
string s
while (getline(cin, s)) ----一直读取到输入结束
while (cin>>s) -----每个单词读取,空格断开(开头的空格不读取) 

string类的访问和修改

  • 可以用数组的形式访问和修改string
#include <iostream>
#include <string>
using namespace std;
int main(){
    string s = "1234567890";
    for(int i=0,len=s.length(); i<len; i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;   
    s[5] = '5';
    cout<<s<<endl;
    return 0;
}

输出

1 2 3 4 5 6 7 8 9 0
1234557890
  • 拼接:用+来拼接字符串
string s1 = "first ";
string s2 = "second ";
string s5 = s1 + s2;
  • 插入:格式string1.insert(position, string2)
string s1='12345';
string s2='ss';
s1.insert(2,s2);
cout<< s1 <<endl;

输出

12ss345
  • 删除字符串:string1.erase(position, delete_length)
    position表示开始删除的字符位置;delete_length表示需要删除的字符长度,如果不写,会删除到该字符串结束
int main(){
    string s1, s2, s3;
    s1 = s2 = s3 = "1234567890";
    s2.erase(5);
    s3.erase(5, 3);
    cout<< s1 <<endl;
    cout<< s2 <<endl;
    cout<< s3 <<endl;
    return 0;
}

输出

1234567890
12345
1234590
  • 提取字符串:string1.substr(position, length)
    position:开始提取的字符串位置;length:提取的字符串长度
string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6);

输出

second s1不发生改变
  • 查找字符串:string1.find(string2, strart_position)
  • find_first_of() 函数:查找字符串共同所有的字符首次出现的位置

6. 表达式

作用域符号::

A::member就表示类A中的成员member

if

  • 只有else if,没有elif;
  • 注意分号的位置
if case1
	do;
else if case2
	do;
else
	do;

7. 指针

指针是一个变量,其值为另一个变量的地址type *var_name;

#include <iostream>
 
using namespace std;
 
int main ()
{
   int  var = 20;   // 实际变量的声明
   int  *ip;        // 指针变量的声明
 
   ip = &var;       // 在指针变量中存储 var 的地址
 
   cout << "Value of var variable: ";
   cout << var << endl;
 
   // 输出在指针变量中存储的地址
   cout << "Address stored in ip variable: ";
   cout << ip << endl;
 
   // 访问指针中地址的值
   cout << "Value of *ip variable: ";
   cout << *ip << endl;
 
   return 0;
}


Value of var variable: 20
Address stored in ip variable: 0xbfc601ac
Value of *ip variable: 20

指针类型

  • 空指针 *pr=NULL,pr=0
    所有初始化未定义的指针都指向空
  • 指针的算术运算:递增变量指针,以便顺序访问数组中的每一个元素
  • 数组与指针
#include <iostream>
 
using namespace std;
const int MAX = 3;


 例子1:
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指针中第一个元素的地址
   ptr = var;
   ptr++
}

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
例子2:
int main ()
{
   int  var[MAX] = {10, 100, 200};
 
   for (int i = 0; i < MAX; i++)
   {
      *var = i;    // 这是正确的语法,数组名var本身就是地址
      var++;       // 修改 var 的值是非法的。这是因为 var 是一个指向数组开头的常量,不能作为左值。
   }
   return 0;
}

8、输入输出

fstream

  • std::ifstream ifs;

  • ifs.open(argv[2], std::ios_base::binary); //以二进制形式打开文件argv[2]
    //ifs.seekg()对输入文件定位,它有两个参数:第一个参数是偏移量,第二个参数是基地址。

  • ifs.seekg(0, ifs.end); //基地址为文件结束处,偏移地址为0,于是指针定位在文件结束处
    //ifs.tellg() 不需要带参数,返回当前定位指针的位置,也代表着输入流的大小。

  • size_t fsize = ifs.tellg(); //sp为定位指针,因为它在文件结束处,所以也就是文件的大小

  • ifs.seekg(0, ifs.beg); ///基地址为文件头,偏移量为0,于是定位在文件头
    //ios::beg:表示输入流的开始位置
    //ios::cur:表示输入流的当前位置
    //ios::end:表示输入流的结束位置

9. 类

本章节主要参考c语言中文网,好评!

9.1 类的声明

  • 类的定义,相当于定义一种新的数据格式,等同于int a; float b等声明;
  • 创建对象(等同于常说的变量名)Student liLei;
  • 类中有成员函数,成员变量;其中:成员函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前。
#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;  //成员变量
      void setWidth( double wid ); //成员函数
      double getWidth( void );
 
   private:
      double width;
};
 
// 成员函数定义
double Box::getWidth(void)
{
    return width ;
}
 
void Box::setWidth( double wid )
{
    width = wid;
}
 
// 程序的主函数
int main( )
{
   Box box;
 
   // 不使用成员函数设置长度
   box.length = 10.0; // OK: 因为 length 是公有的
   cout << "Length of box : " << box.length <<endl;
 
   // 不使用成员函数设置宽度
   // box.width = 10.0; // Error: 因为 width 是私有的
   box.setWidth(10.0);  // 使用成员函数设置宽度
   cout << "Width of box : " << box.getWidth() <<endl;
 
   return 0;
}
9.2 成员访问限定符 private/public/protect
  • 如果既不写 private 也不写 public,就默认为 private
  • 类中向外暴露的接口(能通过对象访问的成员)都声明为 public
  • private 关键字的作用在于更好地隐藏类的内部实现,只在类内调用,或者通过类的public 成员函数调用,而不能通过对象直接调用
  • 成员变量大都以m_开头,这是约定成俗的写法,不是语法规定的内容。以m_开头既可以一眼看出这是成员变量,又可以和成员函数中的形参名字区分开。
  • 正确的示例
#include <iostream>
using namespace std;

//类的声明
class Student{
private:  //私有的
    char *m_name;
    int m_age;
    float m_score;

public:  //共有的
    void setname(char *name);
    void setage(int age);
    void setscore(float score);
    void show();
};

//成员函数的定义
void Student::setname(char *name){
    m_name = name;
}
void Student::setage(int age){
    m_age = age;
}
void Student::setscore(float score){
    m_score = score;
}
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}

int main(){
    //在栈上创建对象
    Student stu;
    stu.setname("小明"); //通过成员函数给私有变量m_name赋值
    stu.setage(15);
    stu.setscore(92.5f);
    stu.show();

    //在堆上创建对象
    Student *pstu = new Student;
    pstu -> setname("李华");
    pstu -> setage(16);
    pstu -> setscore(96);
    pstu -> show();

    return 0;
}
  • 错误示例
Student stu;
//m_name、m_age、m_score 是私有成员变量,不能在类外部通过对象访问
stu.m_name = "小明";
stu.m_age = 15;
stu.m_score = 92.5f;
stu.show();
9.3 构造函数
  • 定义:它的名字和类名相同,没有返回值,也不能被用户调用
  • ==构造函数没有返回值!!!==因此(1)不管是声明还是定义,函数名前面都不能出现返回值类型,即使是 void 也不允许;(2)函数体中不能有 return 语句。
  • 如果没有构造函数,需要多次调用getname,setname等才能为类内成员变量赋值;通过构造函数,可以在创建对象的同时为成员变量赋值;
  • 示例(结合上一小节代码阅读)
//定义构造函数
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
int main()
{
    //创建对象时向构造函数传参
    Student stu("小明", 15, 92.5f);
 }
9.3.1 构造函数的重载
  • 一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数。(一个对象只会调用一个构造函数,且创建对象时提供的实参必须和其中的一个构造函数匹配)
  • 调用没有参数的构造函数也可以省略括号
  • 默认构造函数:在调用时不需要传入实参的构造函数,包括(1)构造函数没有形参;(2)构造函数在定义行参的时候赋了初值;如果类内没有定义构造函数,编译器自动生成一个,称之为默认构造函数。
9.3.2 构造函数的参数初始化列表
  • 定义:
  • 格式:class_name::Constructor_name(形参列表):初始化参数列表 {函数体}
//采用参数初始化表
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    //TODO:
}
  • 注释:m_name(name), m_age(age), m_score(score)相当于函数体内部的m_name = name; m_age = age; m_score = score;,也是赋值的意思。
  • 注意:
    (1)初始化参数列表要和类中成员变量的顺序保持一致,否则赋值会发生错误;
    (2)初始化 const 成员变量的唯一方法就是使用参数初始化表。
9.4 析构函数

定义:一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行;名字与类名相同,格式~class_name()

9.5 对象指针
  • 在栈上创建对象,形式和定义普通变量类似
Student stu;  //定义一个Student类型的对象
Student *pStu = &stu; //pStu 是一个指针,它指向 Student 类型的数据
  • 在堆上创建对象,必须要用一个指针指向它,记得 delete 掉不再使用的对象。
Student *pStu = new Student; 
//使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。
  • 通过对象名字访问成员使用点号 .,通过对象指针访问成员使用箭头->
9.6 this指针
  • 定义:this是一个关键字,它指向当前对象,通过它可以访问当前对象的所有成员,包括 private、protected、public 属性的。
  • 也是一个 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
  • this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。是成员函数和成员变量关联的桥梁。
  • 只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用(后续会讲到 static 成员)。
#include <iostream>
using namespace std;

class Student{
public:
    void setname(char *name);
    void setage(int age);
    void setscore(float score);
    void show();
private:
    char *name;
    int age;
    float score;
};

void Student::setname(char *name){
    this->name = name;
}
void Student::setage(int age){
    this->age = age;
}
void Student::setscore(float score){
    this->score = score;
}
void Student::show(){
    cout<<this->name<<"的年龄是"<<this->age<<",成绩是"<<this->score<<endl;
}

int main(){
    Student *pstu = new Student;
    pstu -> setname("李华");
    pstu -> setage(16);
    pstu -> setscore(96.5);
    pstu -> show();

    return 0;
}

运行结果李华的年龄是16,成绩是96.5

9.7 inline函数
  • 作用:函数之间的跳转是有时间和空间的开销,将那些短小的、频繁调用的函数声明为内联函数,可以消除函数调用的时空开销。
  • 在类体中和类体外定义成员函数是有区别的:在类体中定义的成员函数会自动成为内联函数(不需要加inline),在类体外定义的不会;
  • 如果既希望将函数定义在类体外部,又希望它是内联函数:在类体外定义函数时加 inline 关键字,声明时添加关键字无效
    举个例子
//声明内联函数
void swap1(int *a, int *b);  //也可以添加inline,但编译器会忽略
//定义内联函数
inline void swap1(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
  • Tips: 只有当函数只有 10 行甚至更少时才将其定义为内联函数(不允许有循环和开关语句),主要是为了程序运行的效率问题
//函数定义,这样say就变成一个内联函数
inline void Student::say(){
    cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
9.8 static静态成员变量
  • 定义:静态成员变量是一种特殊的成员变量,它被关键字static修饰,实现多个对象共享数据的目标
  • static 成员变量不占用对象的内存,而是在所有对象之外(全局数据区)开辟内存,即使不创建对象也可以访问。如果没有初始化赋值,默认是零;而对于堆区/栈区的数据,如果没有初始赋值,会默认是垃圾数据;
  • 注意: static 成员变量必须在类声明的外部初始化,格式type class::name = value;,比如int Student::m_total = 0;,静态成员变量在初始化时不能再加 static,但必须要有数据类型。被 private、protected、public 修饰的静态成员变量都可以用这种方式初始化。
  • static变量可以通过对象名访问,也可以通过类名访问,但要遵循private/public的访问原则
//通过类类访问 static 成员变量
Student::m_total = 10;
//通过对象来访问 static 成员变量
Student stu("小明", 15, 92.5f);
stu.m_total = 20;
//通过对象指针来访问 static 成员变量
Student *pstu = new Student("李华", 16, 96);
pstu -> m_total = 20;
9.9 静态成员函数
  • 静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
9.10 const关键字
const常量/常函数/const对象
  • 定义:const修饰的成员函数or成员变量不能被修改,
  • const 成员变量:只能通过参数初始化列表去初始化
  • const 成员函数:可以使用类中的所有成员变量,但是不能修改它们的值,需要在声明和定义的时候在函数头部的结尾加上 const 关键字
//类内声明常成员函数
 char *getname() const;
//定义常成员函数
char * Student::getname() const{
    return m_name;
}
  • const 也可以用来修饰对象,称为常对象。一旦将对象定义为常对象之后,就只能调用类的 const 成员了。
const对象
const  class  object(params);
const指针
const class *p = new class(params);
9.11 拷贝函数
#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数(默认构造函数和析构函数即使不写,在编译的时候也会自动生成)
 
   private:
      int *ptr;
};
 
// 成员函数定义,包括构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存,为类中私有变量赋值,根据函数自行定义,不是必须的
    ptr = new int;
    *ptr = len;
}
 
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}
 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
   // 在跳出函数之前,执行class Line的析构函数
}
 
// 程序的主函数
int main( )
{
   Line line(10);   
   // 调用构造函数 Line( int len );      执行  cout << "调用构造函数" << endl;
 
   display(line);   // Line obj=line, obj的对象是类 Line, 复制给line,因此是复制构造函数
 
   return 0;
}
9.12 friend友元函数和友元类
  • 定义:可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。
  • 注意:友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象
  • (1)当作全局函数用法示例,注意传的分别是&stu和pstu
#include <iostream>
using namespace std;

class Student{
public:
    Student(char *name, int age, float score);
public:
    friend void show(Student *pstu);  //将show()声明为友元函数
private:
    char *m_name;
    int m_age;
    float m_score;
};

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }

//非成员函数,不能直接访问类的成员,而是通过对象
void show(Student *pstu){
    cout<<pstu->m_name<<"的年龄是 "<<pstu->m_age<<",成绩是 "<<pstu->m_score<<endl;
}

int main(){
    Student stu("小明", 15, 90.6);
    show(&stu);  //调用友元函数,通过对象,本身是传地址
    Student *pstu = new Student("李磊", 16, 80.5);
    show(pstu);  //调用友元函数

    return 0;
}

(2)将其他类的成员函数声明为友元函数

  • 定义:一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员
#include <iostream>
using namespace std;
****要在Student中使用Address类,所以提前声明*****
class Address;  //提前声明Address类

//声明Student类
class Student{
public:
    Student(char *name, int age, float score);
public:
	****Student 类的成员函数 show() 声明为 Address 类的友元函数*******
	*****show() 就可以访问 Address 类的 private 成员变量了****
    void show(Address *addr);
private:
    char *m_name;
    int m_age;
    float m_score;
};

//声明Address类
class Address{
private:
    char *m_province;  //省份
    char *m_city;  //城市
    char *m_district;  //区(市区)
public:
    Address(char *province, char *city, char *district);
    //将Student类中的成员函数show()声明为友元函数
    friend void Student::show(Address *addr);
};

//实现Student类
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(Address *addr){
    cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
    cout<<"家庭住址:"<<addr->m_province<<"省"<<addr->m_city<<"市"<<addr->m_district<<"区"<<endl;
}

//实现Address类
Address::Address(char *province, char *city, char *district){
    m_province = province;
    m_city = city;
    m_district = district;
}

int main(){
    Student stu("小明", 16, 95.5f);
    Address addr("陕西", "西安", "雁塔");
    stu.show(&addr);
   
    Student *pstu = new Student("李磊", 16, 80.5);
    Address *paddr = new Address("河北", "衡水", "桃城");
    pstu -> show(paddr);

    return 0;
}

注意:关于友元,有两点需要说明

  • 友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
  • 友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。

模板

函数模板
  • 定义:函数模板实际上是一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型
using namespace std;
********template<typename T>被称为模板头*************
*********语法规定:其中typename 也可以替换成class,是一样的(是早期不严谨的用法,现基本用typename***********
template <typename 类型参数1 , typename 类型参数2 , ...> 返回值类型  函数名(形参列表){
    //在函数体中可以使用类型参数
}



***********函数模板的定义**************
template<typename T> void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
}

************也有先声明,再定义的**************
//声明函数模板
template<typename T> T max(T a, T b, T c);
类模板
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++语法 的相关文章

  • 用 C++ 进行服装建模 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在编写一些软件 最终会绘制一个人体框架 可以配置各种参数 并且计划是在假人身上放置某种衣服 我研究
  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • 如何读取扩展文件属性/文件元数据

    因此 我按照教程使用 ASP net core 将文件 上传 到本地路径 这是代码 public IActionResult About IList
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • C++中的类查找结构体数组

    我正在尝试创建一个结构数组 它将输入字符串链接到类 如下所示 struct string command CommandPath cPath cPathLookup set an alarm AlarmCommandPath send an
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 用于检查项目文件中的项目变量和引用路径的 api

    我正在研究一个 net application VS2010 与 x 没有 解和变量号这些解决方案中的项目数量 我需要检查项目属性 特定于一定数量的项目 是否同质 并且检查 验证构建期间的参考路径 有没有一个API是这样的吗 如果没有 我该
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • 单击 form2 上的按钮触发 form 1 中的方法

    我对 Windows 窗体很陌生 我想知道是否可以通过单击表单 2 中的按钮来触发表单 1 中的方法 我的表格 1 有一个组合框 我的 Form 2 有一个 保存 按钮 我想要实现的是 当用户单击表单 2 中的 保存 时 我需要检查表单 1
  • Rx 中是否有与 Task.ContinueWith 运算符等效的操作?

    Rx 中是否有与 Task ContinueWith 运算符等效的操作 我正在将 Rx 与 Silverlight 一起使用 我正在使用 FromAsyncPattern 方法进行两个 Web 服务调用 并且我想这样做同步地 var o1
  • 在一个字节中存储 4 个不同的值

    我有一个任务要做 但我不知道从哪里开始 我不期待也绝对不想要代码中的答案 我想要一些关于该怎么做的指导 因为我感到有点失落 将变量打包和解包到一个字节中 您需要在一个字节中存储 4 个不同的值 这些值为 NAME RANGE BITS en
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 私有模板函数

    我有一堂课 C h class C private template
  • 如何在 C# 中调整图像大小同时保持高质量?

    我从这里找到了一篇关于图像处理的文章 http www switchonthecode com tutorials csharp tutorial image editing saving cropping and resizing htt
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi
  • Linq-to-entities,在一个查询中获取结果+行数

    我已经看到了有关此事的多个问题 但它们已经有 2 年 或更长 的历史了 所以我想知道这方面是否有任何变化 基本思想是填充网格视图并创建自定义分页 所以 我还需要结果和行数 在 SQL 中 这将类似于 SELECT COUNT id Id N
  • 当另一个线程可能设置共享布尔标志(最多一次)时,是否可以读取共享布尔标志而不锁定它?

    我希望我的线程能够更优雅地关闭 因此我尝试实现一个简单的信号机制 我不认为我想要一个完全事件驱动的线程 所以我有一个工作人员有一种方法可以使用关键部分优雅地停止它Monitor 相当于C lock我相信 绘图线程 h class Drawi

随机推荐

  • Java和Python读取文件总结

    public static ArrayList
  • 紫光电子档案管理系统存在SQL注入漏洞(漏洞复现)

    文章目录 紫光电子档案管理系统存在SQL注入漏洞 漏洞复现 0x01 前言 0x02 漏洞描述 0x03 影响范围 0x04 漏洞环境 0x05 漏洞复现 1 访问漏洞环境 2 构造POC 3 复现 紫光电子档案管理系统存在SQL注入漏洞
  • VMware如何导出和导入OVF文件

    开源虚拟化格式 OVF文件 是一种开源的文件规范 是一种开源 安全 有效 可拓展的便携式虚拟打包格式 由ovf文件 mf文件 cert文件 vmdk文件和iso文件等组成 可以用于虚拟机在不同虚拟化平台上的迁移 本文介绍VMware如何导出
  • Windows10访问共享总是提示输入网络凭证不正确

    场景 安装了windows10系统后 访问共享时总是提示输入网络凭证 输入什么都提示不正确 被访问机也是windows 10 操作系统 使用windows7不需要输入密码就可以访问 即使在被访问机上增加新的用户或者用guest账号去登录也会
  • GB9706.1-2007名词解释:电压

    电压 1 高电压 任何超过 1000V 交流或 1500V 直流或 1500V 峰值的电压 2 网电源电压 多相供电网中两相线之间的电压 或单相供电网中相线与中性线之间的电压 3 安全特低电压 在用安全特低电压变压器或等效隔离程度的装置与供
  • typora

    关于typora的一篇博客 https blog csdn net mingzhuo 126 article details 79941450
  • faceswap使用记录

    1 没有显示 fs cache文件夹 当时我是使用云gpu来运行文件代码的 里面提示我安装两个配置文件放置到 fs cache文件夹 但是当前文件夹里面并没有显示 fa cache文件夹 虽然不知道是什么原因 但这个文件夹其实是存在的 你下
  • java读取文件的方法是_java读取文件的方法有几种

    java读取文件的方法有几种 发布时间 2020 06 26 14 56 33 来源 亿速云 阅读 104 作者 Leah 这篇文章将为大家详细讲解有关java读取文件的方法 文章内容质量较高 因此小编分享给大家做个参考 希望大家阅读完这篇
  • 【python】Counter 函数的用法

    https docs python org 3 6 library collections html collections Counter 原文链接 https blog csdn net u010339879 article detai
  • 使用CocosBuilder2.1结合cocos2d-x2.0.3创建动画场景

    原文地址 http article ityran com archives 2140 本为由泰然教程组成员 浅底 原创 作为一位经验丰富的游戏开发人员 这次浅底将CocosBuilder经验分享给大家 希望大家喜欢 欢迎拍砖 转载请注明出处
  • yolov5加入CBAM,SE,CA,ECA注意力机制,纯代码(22.3.1还更新)

    本文所涉及到的yolov5网络为5 0版本 后续有需求会更新6 0版本 CBAM注意力 class ChannelAttention nn Module def init self in planes ratio 16 super Chan
  • 答辩准备细节 - 推荐第三本书很棒的书

    之前介绍了 PPT演讲力 重要时刻 不要输在表达上 和 金字塔原理 本次准备来介绍一本非常好的设计书 写给大家看的设计书 01 设计的必要性 写简历 做PPT等都可以用到 我们虽然不是专门的设计人员 但是我们仍然可以追求内容更好看 人们对于
  • XDOJ寻找最长的行

    寻找最长的行 类别 字符串 时间限制 1S 内存限制 1000Kb 问题描述 寻找若干行文本中最长的一行 输入说明 输入为多个字符串 每个字符串长度不超过100个字符 每个字符串占一行 输入的行为 end 时表示输入结束 输出说明 输出其中
  • Elasticsearch 实现分组统计

    之前有个查询es分组求和的需求 类似关系型数据库 select a b sum c from table group by a b 当时用DSL查询语句实现 这边记录下 GET my index my type search from 0
  • C++中new与delete问题学习

    C 中new与delete问题学习 一 new char与delete问题 1 问题程序 include
  • 普歌-云言团队-Spring的AOP简介

    什么是AOP AOP 为 Aspect Oriented Programming 的缩写 意思为面向切面编程 是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 AOP 是 OOP 的延续 是软件开发中的一个热点 也是Spri
  • 【ES实战】索引mapping的动态设置

    动态mapping 文章目录 动态mapping 动态mapping规则 语法规则 match mapping type match and unmatch match pattern path match and path unmatch
  • signature=8a03839902ac8eb66fcf33ab62032d86,swch-20200612

    0001710583 20 000020 txt 20200618 0001710583 20 000020 hdr sgml 20200618 20200618161953 ACCESSION NUMBER 0001710583 20 0
  • 了解Nginx配置文件结构与配置上下文

    提供 ZStack云计算 系列教程 本教程为如何在Ubuntu 14 04上实现Nginx与LEMP系列四篇中的第四篇 内容介绍 Nginx是一套高性能Web服务器 负责处理互联网上各大型站点的日常负载 其特别擅长处理高并发连接与大量静态内
  • c++语法

    文章目录 0 0 编译运行 单个程序编辑调试 库文件编译调试 1 变量 1 1 变量的声明和定义 1 2 变量的作用域 1 3 namespace命名空间 标准空间std 2 关键字 2 1 extern 3 常量 1 define 定义