C++之类内不同的数据成员类型以及方法(静态,const,引用,常量引用)

1、C++中可以有不同的数据成员类型,这些成员类型有不同的作用,最常见的数据成员类型分别是:静态数据成员,常量数据成员,引用数据成员以及常量引用数据成员。

1.1 先看静态数据成员。静态数据成员的声明方法是:

class myClass

{

public:

void printCount();

private:

static int count;

};

静态数据成员既可以在类方法内访问静态数据成员,也可以在类方法外部访问静态数据成员。在类方法内部访问静态成员时和使用普通的成员变量没有区别,例如我们可以这样定义printCount()函数:

void printCount()

{

cout<<count<<endl;

count++;

}

当我们在类方法外部访问静态数据成员时,是这么用的(此时,count应该定义为public):

int yourCount=myClass::count;

用法知道了以后就是静态的作用,静态数据成员是所有对象共有的数据成员,所有对象都可对它进行操作。假设myClass有两个对象分别是a和b,a执行myCount()之后,输出的是0,b执行myCount()之后输出的是1,a再执行myCount()之后输出的就是2;

1.2  接下来是常量数据成员。类中的数据成员可以声明为const,意味着在创建并初始化以后,数据成员的值就不能再改变。这是为什么当类定义常量数据成员时我们应该使用构造函数初始化器来初始化数据成员的原因。如果某个常量只适用于类,那么应该使用静态常量数据成员,而不是全局常量。

1.3  接下来是引用数据成员。引用数据成员需要使用构造函数初始化器才能使用,因为如果引用对象不存在,就没办法使用引用,而且引用一旦初始化,就不能改变引用的对象,因此不可能在赋值运算符中对引用赋值。

1.4  最后是常量引用数据成员。这个就是引用数据成员和常量数据成员的结合。值得注意的是,常量引用数据成员只能调用常量方法,不能调用非常量方法。



2、 同样的,C++可以有不同的方法,主要有静态方法,const方法内联方法等等。

2.1  先说静态方法静态方法是在类中使用staitc修饰的方法,在类定义的时候已经被装载和分配。而非静态方法是不加static关键字的方法,在类定义时没有占用内存,只有在类被实例化成对象时,对象调用该方法才被分配内存。静态方法的定义是这样的:

class myClass

{

public:

static void printCount()

{

cout<<count<<endl;

count++;

}

private:

static int count;

int yourID;

};

可以看到此时printCount()方法被定义为静态方法。静态方法不属于特定对象,因此没有this指针,当用某个特定对象调用静态方法时,静态方法不会访问这个对象的非静态数据成员。实际上,静态方法就像一个普通的函数一样,只不过这个静态方法只能访问类的静态数据成员或者静态方法。使用类的静态方法就像使用静态数据成员一样,在类方法内部,就像普通方法一样,在类方法外部则要使用作用域限定符。

2.2  接下来说const方法。常量对象的值不能改变。如果使用常量对象、常量对象的引用和指向常量对象的指针,编译器将不允许调用对象的任何方法,除非这些方法承诺不改变任何数据成员。为了不改变数据成员,可以用const关键字标记方法本身。

class myClass

{

public:

const void printID()

{

cout<<yourID<<endl;

}

private:

static int count;

int yourID;

};

将方法标记为const,就是与客户代码立下了契约,承诺在方法内部不会改变对象内部的值。如果将实际上修改了数据成员的方法声明为const,编译器将会报错。不能将静态方法声明为const,因为这是多余的。静态方法没有类的实例,因此也就不存在对象内部的值。const的工作原理是将方法内部用到的数据成员都标记为const引用,因此如果试图修改数据成员,编译器就会报错。值得一提的是,非const对象可以调用const方法非const方法,然而,const对象只能调用const方法。但是有时候我们需要在const方法内部改变一些对对象无关的值,如果我们去修改了,编译器又会认为这样做是错误的,编译就会不通过,这时候我们需要将变量声明为mutable,例如:

class myClass

{

public:

const void printID()

{

cout<<yourID<<endl;

count++;

}

private:

mutable int count=0;

int yourID;

};

每调用一次printID()方法,count就会自增1,我们将count声明为mutable(中文意思:可变的、会变的,就是告诉编译器在const()方法内部允许改变这个值)就可以在const方法内部改变count的值而编译器也会通过。

2.3 再说说方法重载

我们都知道,一个类可以有多个构造函数,只不过这些构造函数的参数数量或者参数类型应该不一样。在C++中,可以对任何方法这么做,这就是重载。例如:

class myClass

{

public:

void add(int i,int j)

{

cout<<"Two Integers' addition i + j = "<<i+j<<endl;

}

void add(double i,double j)

{

cout<<"Two Doubles' addition i + j = "<<i+j<<endl;

}

};

当我们这样用时:add(1,2),就调用了第一个add方法,当我们使用add(1.0,2.1)时就用了第二个方法。需要注意的是,不应该将返回值的不同作为重载的依据。另外,可以根据const重载方法,也就是说可以编写两个名称相同参数也相同的方法,只不过其中一个是const方法,另一个不是。如果是const对象就调用const方法,如果不是const对象就调用非const方法。重载方法可以被显式的删除,用这种方法禁止调用具有特定参数的成员函数。例如考虑下面的函数:

class myClass{

public:

void foo(int i);

};

我们用这个方法的时候一般这么用foo(1);这个当然是可以运行的,但是foo(1.2)也可以运行,因为1.2这个double类型的数会被强制转换为int类型,这样就运行了,我们可以阻止这种转换,例如:

class myClass{

public:

void foo(int i);

void foo(int i)= delete;

};

通过这一改动,用double做参数调用foo()时,编译器会给出错误提示,而不是将其转换为整数。

此外还有,默认参数还有内联方法不赘述了。




<think>嗯,我现在要理解C++静态常量整型数据成员内初始化的规则。首先,根据之前看到的引用内容,好像C++11之前的标准只允许静态常量整型成员内初始化。那为什么会这样呢? 我记得在引用[4]中提到,定义只是一个框架,不会分配存储空间,所以非静态成员内初始化可能有问题。但静态成员是属于的,不是对象实例的一部分,所以可能处理方式不同。而常量整型可能因为编译时就能确定值,所以允许在内初始化。 比如引用[1]说,静态成员必须在外初始化,但静态常量整型可以在内初始化。这可能是因为这些类型的值在编译期就能确定,不需要运行时计算,所以编译器可以处理。而非整型或者非静态的可能需要更复杂的内存分配,所以不能在内初始化。 然后引用[2]和[3]的例子显示,静态成员需要在外初始化,除了静态常量整型。比如在base中,const static int c可以在内初始化为9,而static int b需要在外初始化。而引用[4]提到的错误C2864说明在C++11之前,非静态常量整型也不能在内初始化,只能用构造函数初始化列表或者在C++11之后支持内初始化。 所以总结一下,规则可能如下: 1. 静态常量整型(static const int,或者枚举类型)可以在内初始化。 2. 非静态常量成员必须在构造函数初始化列表里初始化。 3. C++11之后,允许非静态成员内初始化,但之前不行。 4. 静态非整型或非常量成员必须在外定义和初始化。 那为什么会允许静态常量整型在内初始化呢?可能是因为它们既是常量又是整型,编译期就能确定值,不需要分配存储空间,似于符号常量。而其他类型静态成员需要分配存储空间,所以必须外定义。 比如,在内声明static const int size = 100; 这样的成员可以作为数组的大小,因为编译器需要知道这个值在编译期。而如果是static const double,可能就不允许,除非使用C++11或更高版本? 不过根据引用[2],例子中的const static int c=9是正确的,而引用[4]提到VS2010不支持非静态内初始化,所以可能整型包括枚举,但不包括其他类型如浮点数或字符串。 另外,C++11扩展了内初始化的支持,允许非静态成员在声明时初始化,所以现在可能有更多的灵活性。但在旧标准中,只有静态常量整型可以这样做。 这样看来,规则的核心在于存储空间的分配和编译时的确定性。静态常量整型在编译时就能确定,所以不需要在外分配存储空间,而其他类型静态成员需要在外定义,因为它们需要存储空间,并且可能涉及更复杂的初始化过程。 例如,引用[3]中的例子显示,非常量静态成员必须在外初始化,因为它们不是常量,无法在编译时确定值,必须有一个单独的定义来分配存储空间。而静态常量整型可以作为编译时常量,不需要单独的定义,除非需要取地址等情况。 但是,如果静态常量整型被odr-used(比如取地址),可能还是需要在外定义。否则,可能不需要。这可能是一个例外情况。 总的来说,C++的规则在这里是为了平衡编译器的能力和灵活性,确保成员的正确初始化和存储分配。</think>### C++静态常量整型数据成员内初始化规则解析 #### 1. **核心规则** 在**C++11标准之前**,**只有静态常量整型数据成员**(`static const integral type`)才允许在内直接初始化[^4]。例如: ```cpp class Example { public: static const int MAX_SIZE = 100; // ✔️ 合法 static const double PI = 3.14; // ❌ 非法(非整型) }; ``` - **静态性**:成员属于而非对象,无需实例化即可使用。 - **常量性**:值在编译期确定,不可修改。 - **整型类型**:包括`int`、`char`、`enum`及其`const`修饰的变体[^4]。 #### 2. **技术原理** - **存储分配**:定义不分配内存,静态常量整型因其**编译期可计算性**,可视为符号常量,无需额外存储[^1]。 - **语法兼容性**:允许用此成员定义数组大小等场景,例如: ```cpp class Buffer { public: static const int SIZE = 1024; char data[SIZE]; // 直接使用内初始化的静态常量 }; ``` #### 3. **例外与限制** - **ODR-Use(单一定义规则使用)**:若需取地址或绑定引用,仍需在外定义(但无需重复初始化): ```cpp // 内声明 class Example { public: static const int VALUE = 42; }; // 外定义(仅需一次) const int Example::VALUE; // 不需要赋值 ``` - **C++11扩展**:允许非静态成员(含非整型)内初始化[^4]: ```cpp class PostCpp11 { public: int count = 0; // ✔️ 合法(C++11+) double ratio{1.618}; // ✔️ 合法(C++11+) }; ``` #### 4. **错误示例与修复** - **错误代码(C++11前)**: ```cpp class ErrorClass { public: static int id = 10; // ❌ 非常量 const float PI = 3.14; // ❌ 非静态且非整型 }; ``` - **修复方法**: ```cpp class FixedClass { public: static const int id = 10; // ✔️ 静态常量整型 const float PI; // ✔️ 通过构造函数初始化 FixedClass() : PI(3.14) {} }; ``` #### 5. **对比其他成员类型** | 成员类型 | 内初始化(C++11前) | 外初始化要求 | |---------------------------|-----------------------|----------------------| | 静态常量整型 | ✔️ | 仅ODR-Use时需要 | | 静态常量非整型(如double)| ❌ | ✔️ | | 非静态常量成员 | ❌ | 构造函数初始化列表 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值